diff --git a/DEPS b/DEPS
index 5c3929e..0d76b9c 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': 'e94871bf8308b72ed44353956f1ff6c4ba6e7597',
+  'skia_revision': '5b071782585024bd75a203f3cde0429bfb7ec99d',
   # 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': 'f914089eae2ea692c6ae29d1ee4d1fe8aea59629',
+  'v8_revision': '2a4a00d524586b4dcbd4984c8de24ab57d40203d',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b0860beccd6a4a8d9f8ea3dbba392a3a13218ad3',
+  'pdfium_revision': '827f6ff220edea40252e52d128662d37a591fdb9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -166,7 +166,7 @@
     Var('chromium_git') + '/external/leveldb.git' + '@' + '09a3c8e7417547829b94bcdaa62cdf9e896f29a9',
 
   'src/third_party/snappy/src':
-    Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + '77c12adc192ac6620a0f0d340c99149ec56a97a3',
+    Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + 'b02bfa754ebf27921d8da3bd2517eab445b84ff9',
 
   'src/tools/gyp':
     Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
@@ -208,7 +208,7 @@
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '6b9c691dafc884424e05b5294b6e0e4996d533a5',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '30c261b1ebe8f06d687cac5b3b442d51a7839d00',
 
   'src/third_party/ffmpeg':
     Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'dbbdb1680bd8e77ea72f8e5a79a09101bd7a9bdd',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 11f96396c..0118129 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -349,6 +349,8 @@
     "system/bluetooth/bluetooth_notification_controller.cc",
     "system/bluetooth/bluetooth_notification_controller.h",
     "system/bluetooth/bluetooth_observer.h",
+    "system/bluetooth/bluetooth_power_controller.cc",
+    "system/bluetooth/bluetooth_power_controller.h",
     "system/bluetooth/tray_bluetooth.cc",
     "system/bluetooth/tray_bluetooth.h",
     "system/bluetooth/tray_bluetooth_helper.cc",
@@ -1199,6 +1201,7 @@
     "sticky_keys/sticky_keys_overlay_unittest.cc",
     "sticky_keys/sticky_keys_unittest.cc",
     "system/audio/tray_audio_unittest.cc",
+    "system/bluetooth/bluetooth_power_controller_unittest.cc",
     "system/bluetooth/tray_bluetooth_helper_unittest.cc",
     "system/brightness/tray_brightness_unittest.cc",
     "system/date/date_view_unittest.cc",
diff --git a/ash/display/display_configuration_controller.cc b/ash/display/display_configuration_controller.cc
index 0fdee48..ae55f50 100644
--- a/ash/display/display_configuration_controller.cc
+++ b/ash/display/display_configuration_controller.cc
@@ -110,13 +110,14 @@
 void DisplayConfigurationController::SetDisplayRotation(
     int64_t display_id,
     display::Display::Rotation rotation,
-    display::Display::RotationSource source) {
+    display::Display::RotationSource source,
+    DisplayConfigurationController::RotationAnimation mode) {
   if (display_manager_->IsDisplayIdValid(display_id)) {
     if (GetTargetRotation(display_id) == rotation)
       return;
     ScreenRotationAnimator* screen_rotation_animator =
         GetScreenRotationAnimatorForDisplay(display_id);
-    screen_rotation_animator->Rotate(rotation, source);
+    screen_rotation_animator->Rotate(rotation, source, mode);
   } else {
     display_manager_->SetDisplayRotation(display_id, rotation, source);
   }
diff --git a/ash/display/display_configuration_controller.h b/ash/display/display_configuration_controller.h
index e17f140a4..f2e194b 100644
--- a/ash/display/display_configuration_controller.h
+++ b/ash/display/display_configuration_controller.h
@@ -31,6 +31,15 @@
 class ASH_EXPORT DisplayConfigurationController
     : public WindowTreeHostManager::Observer {
  public:
+  // Use SYNC if it is important to rotate immediately after the
+  // |SetDisplayRotation()|. As a side effect, the animation is less smooth.
+  // ASYNC is actually slower because it takes longer to rotate the screen after
+  // a screenshot is taken. http://crbug.com/757851.
+  enum RotationAnimation {
+    ANIMATION_SYNC = 0,
+    ANIMATION_ASYNC,
+  };
+
   DisplayConfigurationController(
       display::DisplayManager* display_manager,
       WindowTreeHostManager* window_tree_host_manager);
@@ -48,7 +57,8 @@
   // Sets the display's rotation with animation if available.
   void SetDisplayRotation(int64_t display_id,
                           display::Display::Rotation rotation,
-                          display::Display::RotationSource source);
+                          display::Display::RotationSource source,
+                          RotationAnimation mode = ANIMATION_ASYNC);
 
   // Returns the rotation of the display given by |display_id|. This returns
   // the target rotation when the display is being rotated.
diff --git a/ash/display/display_configuration_controller_unittest.cc b/ash/display/display_configuration_controller_unittest.cc
index b491db751..a7d04d75 100644
--- a/ash/display/display_configuration_controller_unittest.cc
+++ b/ash/display/display_configuration_controller_unittest.cc
@@ -59,17 +59,31 @@
       display.id(), display::Display::ROTATE_0,
       display::Display::RotationSource::ROTATION_SOURCE_USER);
   old_screen_rotation_animator->Rotate(
-      display::Display::ROTATE_90,
-      display::Display::RotationSource::ROTATION_SOURCE_USER);
+      display::Display::ROTATE_90, display::Display::ROTATION_SOURCE_USER,
+      DisplayConfigurationController::ANIMATION_SYNC);
 
   ScreenRotationAnimator* new_screen_rotation_animator =
       testapi.GetScreenRotationAnimatorForDisplay(display.id());
   new_screen_rotation_animator->Rotate(
-      display::Display::ROTATE_180,
-      display::Display::RotationSource::ROTATION_SOURCE_USER);
+      display::Display::ROTATE_180, display::Display::ROTATION_SOURCE_USER,
+      DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_EQ(old_screen_rotation_animator, new_screen_rotation_animator);
 }
 
+TEST_F(DisplayConfigurationControllerTest, GetTargetRotationWithAnimation) {
+  display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
+  DisplayConfigurationController* controller =
+      Shell::Get()->display_configuration_controller();
+  DisplayConfigurationControllerTestApi testapi(controller);
+  controller->SetDisplayRotation(
+      display.id(), display::Display::ROTATE_180,
+      display::Display::ROTATION_SOURCE_USER,
+      DisplayConfigurationController::ANIMATION_ASYNC);
+  EXPECT_EQ(display::Display::ROTATE_180,
+            controller->GetTargetRotation(display.id()));
+  EXPECT_EQ(display::Display::ROTATE_180, GetDisplayRotation(display.id()));
+}
+
 TEST_F(DisplayConfigurationControllerSmoothRotationTest,
        GetTargetRotationWithAnimation) {
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
@@ -78,7 +92,8 @@
   DisplayConfigurationControllerTestApi testapi(controller);
   controller->SetDisplayRotation(
       display.id(), display::Display::ROTATE_180,
-      display::Display::RotationSource::ROTATION_SOURCE_USER);
+      display::Display::ROTATION_SOURCE_USER,
+      DisplayConfigurationController::ANIMATION_ASYNC);
   EXPECT_EQ(display::Display::ROTATE_180,
             controller->GetTargetRotation(display.id()));
   EXPECT_EQ(display::Display::ROTATE_0, GetDisplayRotation(display.id()));
diff --git a/ash/display/screen_orientation_controller_chromeos.cc b/ash/display/screen_orientation_controller_chromeos.cc
index 4c1e217..27e54f9 100644
--- a/ash/display/screen_orientation_controller_chromeos.cc
+++ b/ash/display/screen_orientation_controller_chromeos.cc
@@ -5,7 +5,6 @@
 #include "ash/display/screen_orientation_controller_chromeos.h"
 
 #include "ash/ash_switches.h"
-#include "ash/display/display_configuration_controller.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/shell.h"
 #include "ash/wm/mru_window_tracker.h"
@@ -212,7 +211,8 @@
   SetRotationLockedInternal(false);
   if (user_rotation_ != current_rotation_) {
     SetDisplayRotation(user_rotation_,
-                       display::Display::ROTATION_SOURCE_ACCELEROMETER);
+                       display::Display::ROTATION_SOURCE_ACCELEROMETER,
+                       DisplayConfigurationController::ANIMATION_SYNC);
   }
 }
 
@@ -343,9 +343,11 @@
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
   if (!display::Display::HasInternalDisplay())
     return;
+
   if (current_rotation_ != user_rotation_) {
     SetDisplayRotation(user_rotation_,
-                       display::Display::ROTATION_SOURCE_ACCELEROMETER);
+                       display::Display::ROTATION_SOURCE_ACCELEROMETER,
+                       DisplayConfigurationController::ANIMATION_SYNC);
   }
   for (auto& observer : observers_)
     observer.OnUserRotationLockChanged();
@@ -353,7 +355,8 @@
 
 void ScreenOrientationController::SetDisplayRotation(
     display::Display::Rotation rotation,
-    display::Display::RotationSource source) {
+    display::Display::RotationSource source,
+    DisplayConfigurationController::RotationAnimation mode) {
   if (!display::Display::HasInternalDisplay())
     return;
   current_rotation_ = rotation;
@@ -361,7 +364,7 @@
       &ignore_display_configuration_updates_, true);
 
   Shell::Get()->display_configuration_controller()->SetDisplayRotation(
-      display::Display::InternalDisplayId(), rotation, source);
+      display::Display::InternalDisplayId(), rotation, source, mode);
 }
 
 void ScreenOrientationController::SetRotationLockedInternal(
diff --git a/ash/display/screen_orientation_controller_chromeos.h b/ash/display/screen_orientation_controller_chromeos.h
index df0387ce..98ef0ff 100644
--- a/ash/display/screen_orientation_controller_chromeos.h
+++ b/ash/display/screen_orientation_controller_chromeos.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 
 #include "ash/ash_export.h"
+#include "ash/display/display_configuration_controller.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "base/macros.h"
@@ -141,8 +142,11 @@
   // Sets the display rotation for the given |source|. The new |rotation| will
   // also become active. Display changed notifications are suppressed for this
   // change.
-  void SetDisplayRotation(display::Display::Rotation rotation,
-                          display::Display::RotationSource source);
+  void SetDisplayRotation(
+      display::Display::Rotation rotation,
+      display::Display::RotationSource source,
+      DisplayConfigurationController::RotationAnimation mode =
+          DisplayConfigurationController::ANIMATION_ASYNC);
 
   void SetRotationLockedInternal(bool rotation_locked);
 
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc
index 9f09a3a..5364a2c 100644
--- a/ash/public/cpp/ash_pref_names.cc
+++ b/ash/public/cpp/ash_pref_names.cc
@@ -129,6 +129,14 @@
 // A dictionary pref that maps wallpaper file paths to their prominent colors.
 const char kWallpaperColors[] = "ash.wallpaper.prominent_colors";
 
+// Boolean pref indicating whether a user has enabled the bluetooth adapter.
+const char kUserBluetoothAdapterEnabled[] =
+    "ash.user.bluetooth.adapter_enabled";
+
+// Boolean pref indicating system-wide setting for bluetooth adapter power.
+const char kSystemBluetoothAdapterEnabled[] =
+    "ash.system.bluetooth.adapter_enabled";
+
 // NOTE: New prefs should start with the "ash." prefix. Existing prefs moved
 // into this file should not be renamed, since they may be synced.
 
diff --git a/ash/public/cpp/ash_pref_names.h b/ash/public/cpp/ash_pref_names.h
index 8135d5ba..4137014 100644
--- a/ash/public/cpp/ash_pref_names.h
+++ b/ash/public/cpp/ash_pref_names.h
@@ -50,6 +50,9 @@
 
 ASH_PUBLIC_EXPORT extern const char kWallpaperColors[];
 
+ASH_PUBLIC_EXPORT extern const char kUserBluetoothAdapterEnabled[];
+ASH_PUBLIC_EXPORT extern const char kSystemBluetoothAdapterEnabled[];
+
 }  // namespace prefs
 
 }  // namespace ash
diff --git a/ash/public/interfaces/user_info.mojom b/ash/public/interfaces/user_info.mojom
index 6fba255..5a86856 100644
--- a/ash/public/interfaces/user_info.mojom
+++ b/ash/public/interfaces/user_info.mojom
@@ -44,4 +44,7 @@
   string display_name;
   string display_email;
   gfx.mojom.ImageSkia avatar;
-};
\ No newline at end of file
+  // True if this user has a newly created profile (first time login on the
+  // device)
+  bool is_new_profile;
+};
diff --git a/ash/rotator/screen_rotation_animator.cc b/ash/rotator/screen_rotation_animator.cc
index 84caa96..326648c 100644
--- a/ash/rotator/screen_rotation_animator.cc
+++ b/ash/rotator/screen_rotation_animator.cc
@@ -163,6 +163,7 @@
       metrics_reporter_(
           base::MakeUnique<ScreenRotationAnimationMetricsReporter>()),
       disable_animation_timers_for_test_(false),
+      // TODO(wutao): remove the flag. http://crbug.com/707800.
       has_switch_ash_disable_smooth_screen_rotation_(
           base::CommandLine::ForCurrentProcess()->HasSwitch(
               switches::kAshDisableSmoothScreenRotation) &&
@@ -194,7 +195,9 @@
   }
 
   rotation_request->old_rotation = current_rotation;
-  if (has_switch_ash_disable_smooth_screen_rotation_) {
+  if (has_switch_ash_disable_smooth_screen_rotation_ ||
+      DisplayConfigurationController::ANIMATION_SYNC ==
+          rotation_request->mode) {
     StartSlowAnimation(std::move(rotation_request));
   } else {
     std::unique_ptr<viz::CopyOutputRequest> copy_output_request =
@@ -433,8 +436,10 @@
   observer->SetActive();
 }
 
-void ScreenRotationAnimator::Rotate(display::Display::Rotation new_rotation,
-                                    display::Display::RotationSource source) {
+void ScreenRotationAnimator::Rotate(
+    display::Display::Rotation new_rotation,
+    display::Display::RotationSource source,
+    DisplayConfigurationController::RotationAnimation mode) {
   // |rotation_request_id_| is used to skip stale requests. Before the layer
   // CopyOutputResult callback called, there could have new rotation request.
   // Increases |rotation_request_id_| for each new request and in the callback,
@@ -445,7 +450,7 @@
       display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_).id();
   std::unique_ptr<ScreenRotationRequest> rotation_request =
       base::MakeUnique<ScreenRotationRequest>(rotation_request_id_, display_id,
-                                              new_rotation, source);
+                                              new_rotation, source, mode);
   target_rotation_ = new_rotation;
   switch (screen_rotation_state_) {
     case IDLE:
diff --git a/ash/rotator/screen_rotation_animator.h b/ash/rotator/screen_rotation_animator.h
index 06b0e53f..c7f4dcec 100644
--- a/ash/rotator/screen_rotation_animator.h
+++ b/ash/rotator/screen_rotation_animator.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "ash/ash_export.h"
+#include "ash/display/display_configuration_controller.h"
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -46,7 +47,8 @@
   // the target position, followed by a new |Rotate()| call with the pending
   // rotation request.
   void Rotate(display::Display::Rotation new_rotation,
-              display::Display::RotationSource source);
+              display::Display::RotationSource source,
+              DisplayConfigurationController::RotationAnimation mode);
 
   void AddScreenRotationAnimatorObserver(
       ScreenRotationAnimatorObserver* observer);
@@ -69,19 +71,23 @@
   using CopyCallback =
       base::OnceCallback<void(std::unique_ptr<viz::CopyOutputResult> result)>;
   struct ScreenRotationRequest {
-    ScreenRotationRequest(int64_t id,
-                          int64_t display_id,
-                          display::Display::Rotation to_rotation,
-                          display::Display::RotationSource from_source)
+    ScreenRotationRequest(
+        int64_t id,
+        int64_t display_id,
+        display::Display::Rotation to_rotation,
+        display::Display::RotationSource from_source,
+        DisplayConfigurationController::RotationAnimation mode)
         : id(id),
           display_id(display_id),
           new_rotation(to_rotation),
-          source(from_source) {}
+          source(from_source),
+          mode(mode) {}
     int64_t id;
     int64_t display_id;
     display::Display::Rotation old_rotation;
     display::Display::Rotation new_rotation;
     display::Display::RotationSource source;
+    DisplayConfigurationController::RotationAnimation mode;
   };
 
   // This function can be overridden in unit test to test removing external
diff --git a/ash/rotator/screen_rotation_animator_unittest.cc b/ash/rotator/screen_rotation_animator_unittest.cc
index aadfbb88..49e4e69 100644
--- a/ash/rotator/screen_rotation_animator_unittest.cc
+++ b/ash/rotator/screen_rotation_animator_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/rotator/screen_rotation_animator.h"
 
 #include "ash/ash_switches.h"
+#include "ash/display/screen_orientation_controller_chromeos.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/public/cpp/config.h"
 #include "ash/rotator/screen_rotation_animator_observer.h"
@@ -263,7 +264,8 @@
   EXPECT_FALSE(observer.notified());
 
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_FALSE(observer.notified());
 
   test_api()->CompleteAnimations();
@@ -279,11 +281,13 @@
   EXPECT_FALSE(observer.notified());
 
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_FALSE(observer.notified());
 
   animator()->Rotate(display::Display::ROTATE_180,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_FALSE(observer.notified());
 
   test_api()->CompleteAnimations();
@@ -295,7 +299,8 @@
 TEST_F(ScreenRotationAnimatorSlowAnimationTest, RotatesToDifferentRotation) {
   SetDisplayRotation(display_id(), display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
   test_api()->CompleteAnimations();
@@ -306,7 +311,8 @@
        ShouldNotRotateTheSameRotation) {
   SetDisplayRotation(display_id(), display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_0,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_FALSE(test_api()->HasActiveAnimations());
 }
 
@@ -316,12 +322,14 @@
 TEST_F(ScreenRotationAnimatorSlowAnimationTest, RotatesDuringRotation) {
   SetDisplayRotation(display_id(), display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(animator()->IsRotating());
   EXPECT_EQ(display::Display::ROTATE_90, animator()->GetTargetRotation());
 
   animator()->Rotate(display::Display::ROTATE_180,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(test_api()->HasActiveAnimations());
   EXPECT_TRUE(animator()->IsRotating());
   EXPECT_EQ(display::Display::ROTATE_180, animator()->GetTargetRotation());
@@ -338,15 +346,18 @@
 TEST_F(ScreenRotationAnimatorSlowAnimationTest, ShouldCompleteAnimations) {
   SetDisplayRotation(display_id(), display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
   animator()->Rotate(display::Display::ROTATE_180,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
   animator()->Rotate(display::Display::ROTATE_270,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
   test_api()->CompleteAnimations();
@@ -371,7 +382,8 @@
       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
   SetDisplayRotation(display_id(), display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_SYNC);
 
   EXPECT_FALSE(GetTray()->visible());
 }
@@ -393,7 +405,8 @@
           base::Unretained(this)));
   SetDisplayRotation(display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   EXPECT_TRUE(animator()->IsRotating());
 
   EXPECT_EQ(display::Display::ROTATE_90, animator()->GetTargetRotation());
@@ -429,7 +442,8 @@
       run_loop_->QuitWhenIdleClosure());
   SetDisplayRotation(secondary_display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
   EXPECT_EQ(primary_display_id, display_manager()->GetDisplayAt(0).id());
@@ -454,7 +468,8 @@
       run_loop_->QuitWhenIdleClosure());
   SetDisplayRotation(primary_display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
   EXPECT_EQ(secondary_display_id, display_manager()->GetDisplayAt(0).id());
@@ -482,7 +497,8 @@
           base::Unretained(this), "640x480"));
   SetDisplayRotation(secondary_display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
   EXPECT_EQ(primary_display_id, display_manager()->GetDisplayAt(0).id());
@@ -512,7 +528,8 @@
           base::Unretained(this), "640x480"));
   SetDisplayRotation(primary_display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
   EXPECT_EQ(secondary_display_id, display_manager()->GetDisplayAt(0).id());
@@ -537,7 +554,8 @@
       run_loop_->QuitWhenIdleClosure());
   SetDisplayRotation(secondary_display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
   EXPECT_EQ(secondary_display_id, display_manager()->GetDisplayAt(0).id());
@@ -573,7 +591,8 @@
           base::Unretained(this)));
   SetDisplayRotation(display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
 
   GetTray()->layer()->GetAnimator()->StopAnimating();
@@ -598,7 +617,8 @@
           base::Unretained(this)));
   SetDisplayRotation(display_id, display::Display::ROTATE_0);
   animator()->Rotate(display::Display::ROTATE_90,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
@@ -613,7 +633,8 @@
 
   // Should work for another rotation.
   animator()->Rotate(display::Display::ROTATE_180,
-                     display::Display::RotationSource::ROTATION_SOURCE_USER);
+                     display::Display::ROTATION_SOURCE_USER,
+                     DisplayConfigurationController::ANIMATION_ASYNC);
   WaitForCopyCallback();
   EXPECT_TRUE(test_api()->HasActiveAnimations());
 
@@ -621,5 +642,4 @@
   EXPECT_FALSE(test_api()->HasActiveAnimations());
   EXPECT_EQ(display::Display::ROTATE_180, GetDisplayRotation(display_id));
 }
-
 }  // namespace ash
diff --git a/ash/session/session_controller.cc b/ash/session/session_controller.cc
index 5c045a9d..b7a7c98 100644
--- a/ash/session/session_controller.cc
+++ b/ash/session/session_controller.cc
@@ -178,6 +178,27 @@
   return active_user_type == user_manager::USER_TYPE_CHILD;
 }
 
+base::Optional<user_manager::UserType> SessionController::GetUserType() const {
+  if (!IsActiveUserSessionStarted())
+    return base::nullopt;
+
+  return base::make_optional(GetUserSession(0)->user_info->type);
+}
+
+bool SessionController::IsUserPrimary() const {
+  if (!IsActiveUserSessionStarted())
+    return false;
+
+  return GetUserSession(0)->session_id == primary_session_id_;
+}
+
+bool SessionController::IsUserFirstLogin() const {
+  if (!IsActiveUserSessionStarted())
+    return false;
+
+  return GetUserSession(0)->user_info->is_new_profile;
+}
+
 bool SessionController::IsKioskSession() const {
   if (!IsActiveUserSessionStarted())
     return false;
diff --git a/ash/session/session_controller.h b/ash/session/session_controller.h
index a639e90ef..045149d 100644
--- a/ash/session/session_controller.h
+++ b/ash/session/session_controller.h
@@ -109,6 +109,18 @@
   // Returns true if the current user is a child account.
   bool IsUserChild() const;
 
+  // Returns the type of the current user, or empty if there is no current user
+  // logged in.
+  base::Optional<user_manager::UserType> GetUserType() const;
+
+  // Returns true if the current user is the primary user in a multi-profile
+  // scenario. This always return true if there is only one user logged in.
+  bool IsUserPrimary() const;
+
+  // Returns true if the current user has the profile newly created on the
+  // device (i.e. first time login on the device).
+  bool IsUserFirstLogin() const;
+
   // Returns true if the current user session is a kiosk session (either
   // chrome app kiosk or ARC kiosk).
   bool IsKioskSession() const;
diff --git a/ash/session/session_controller_unittest.cc b/ash/session/session_controller_unittest.cc
index c1cba24..4721dd2e 100644
--- a/ash/session/session_controller_unittest.cc
+++ b/ash/session/session_controller_unittest.cc
@@ -110,6 +110,7 @@
     session->user_info->account_id = AccountId::FromUserEmail(email);
     session->user_info->display_name = email;
     session->user_info->display_email = email;
+    session->user_info->is_new_profile = false;
 
     controller_->UpdateUserSession(std::move(session));
   }
@@ -476,5 +477,63 @@
   controller->RemoveObserver(&observer);
 }
 
+TEST_F(SessionControllerTest, GetUserType) {
+  // Child accounts
+  mojom::UserSessionPtr session = mojom::UserSession::New();
+  session->session_id = 1u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_CHILD;
+  controller()->UpdateUserSession(std::move(session));
+  EXPECT_EQ(user_manager::USER_TYPE_CHILD, controller()->GetUserType());
+
+  // Regular accounts
+  session = mojom::UserSession::New();
+  session->session_id = 1u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_REGULAR;
+  controller()->UpdateUserSession(std::move(session));
+  EXPECT_EQ(user_manager::USER_TYPE_REGULAR, controller()->GetUserType());
+}
+
+TEST_F(SessionControllerTest, IsUserPrimary) {
+  controller()->ClearUserSessionsForTest();
+
+  // The first added user is a primary user
+  mojom::UserSessionPtr session = mojom::UserSession::New();
+  session->session_id = 1u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_REGULAR;
+  controller()->UpdateUserSession(std::move(session));
+  EXPECT_TRUE(controller()->IsUserPrimary());
+
+  // The users added thereafter are not primary users
+  session = mojom::UserSession::New();
+  session->session_id = 2u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_REGULAR;
+  controller()->UpdateUserSession(std::move(session));
+  // Simulates user switching by changing the order of session_ids.
+  controller()->SetUserSessionOrder({2u, 1u});
+  EXPECT_FALSE(controller()->IsUserPrimary());
+}
+
+TEST_F(SessionControllerTest, IsUserFirstLogin) {
+  mojom::UserSessionPtr session = mojom::UserSession::New();
+  session->session_id = 1u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_REGULAR;
+  controller()->UpdateUserSession(std::move(session));
+  EXPECT_FALSE(controller()->IsUserFirstLogin());
+
+  // user_info->is_new_profile being true means the user is first time login.
+  session = mojom::UserSession::New();
+  session->session_id = 1u;
+  session->user_info = mojom::UserInfo::New();
+  session->user_info->type = user_manager::USER_TYPE_REGULAR;
+  session->user_info->is_new_profile = true;
+  controller()->UpdateUserSession(std::move(session));
+  EXPECT_TRUE(controller()->IsUserFirstLogin());
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index 5710e46..a00739f2 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -104,7 +104,8 @@
     const std::string& display_email,
     user_manager::UserType user_type,
     bool enable_settings,
-    bool provide_pref_service) {
+    bool provide_pref_service,
+    bool is_new_profile) {
   auto account_id = AccountId::FromUserEmail(GetUserIdFromEmail(display_email));
   mojom::UserSessionPtr session = mojom::UserSession::New();
   session->session_id = ++fake_session_id_;
@@ -113,6 +114,7 @@
   session->user_info->account_id = account_id;
   session->user_info->display_name = "Über tray Über tray Über tray Über tray";
   session->user_info->display_email = display_email;
+  session->user_info->is_new_profile = is_new_profile;
   session->should_enable_settings = enable_settings;
   session->should_show_notification_tray = true;
   controller_->UpdateUserSession(std::move(session));
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h
index c2f7ebb..2951173 100644
--- a/ash/session/test_session_controller_client.h
+++ b/ash/session/test_session_controller_client.h
@@ -52,12 +52,14 @@
   // Adds a user session from a given display email. The display email will be
   // canonicalized and used to construct an AccountId. |enable_settings| sets
   // whether web UI settings are allowed. If |provide_pref_service| is true,
-  // eagerly inject a PrefService for this user.
+  // eagerly inject a PrefService for this user. |is_new_profile| indicates
+  // whether the user has a newly created profile on the device.
   void AddUserSession(
       const std::string& display_email,
       user_manager::UserType user_type = user_manager::USER_TYPE_REGULAR,
       bool enable_settings = true,
-      bool provide_pref_service = true);
+      bool provide_pref_service = true,
+      bool is_new_profile = false);
 
   // Simulates screen unlocking. It is virtual so that test cases can override
   // it. The default implementation sets the session state of SessionController
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 862d582d..f29671f 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -59,7 +59,7 @@
 
 // The alpha value used to darken a colorized shelf when the shelf is
 // translucent.
-constexpr int kShelfTranslucentColorDarkenAlpha = 128;
+constexpr int kShelfTranslucentColorDarkenAlpha = 178;
 
 // The alpha vlaue usesd to darken a colorized shelf when the shelf is opaque.
 constexpr int kShelfOpaqueColorDarkenAlpha = 178;
diff --git a/ash/shell.cc b/ash/shell.cc
index 224057b..6bf5e68 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -67,6 +67,7 @@
 #include "ash/shutdown_controller.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/system/bluetooth/bluetooth_notification_controller.h"
+#include "ash/system/bluetooth/bluetooth_power_controller.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
 #include "ash/system/brightness/brightness_controller_chromeos.h"
 #include "ash/system/brightness_control_delegate.h"
@@ -333,6 +334,7 @@
 void Shell::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
   PaletteTray::RegisterLocalStatePrefs(registry);
   WallpaperController::RegisterLocalStatePrefs(registry);
+  BluetoothPowerController::RegisterLocalStatePrefs(registry);
 }
 
 // static
@@ -340,10 +342,10 @@
   LogoutButtonTray::RegisterProfilePrefs(registry);
   NightLightController::RegisterProfilePrefs(registry);
   ShelfController::RegisterProfilePrefs(registry);
-
   // Request access to prefs used by ash but owned by chrome.
   // See //services/preferences/README.md
   TrayCapsLock::RegisterForeignPrefs(registry);
+  BluetoothPowerController::RegisterProfilePrefs(registry);
 }
 
 views::NonClientFrameView* Shell::CreateDefaultNonClientFrameView(
@@ -864,6 +866,9 @@
   shell_port_.reset();
   session_controller_->RemoveObserver(this);
   wallpaper_delegate_.reset();
+  // BluetoothPowerController depends on the PrefService and must be destructed
+  // before it.
+  bluetooth_power_controller_ = nullptr;
   // NightLightController depeneds on the PrefService and must be destructed
   // before it. crbug.com/724231.
   night_light_controller_ = nullptr;
@@ -886,6 +891,8 @@
   if (NightLightController::IsFeatureEnabled())
     night_light_controller_ = base::MakeUnique<NightLightController>();
 
+  bluetooth_power_controller_ = base::MakeUnique<BluetoothPowerController>();
+
   wallpaper_delegate_ = shell_delegate_->CreateWallpaperDelegate();
 
   // Connector can be null in tests.
diff --git a/ash/shell.h b/ash/shell.h
index e8673c81..e414fcb 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -91,6 +91,7 @@
 class AshTouchTransformController;
 class AutoclickController;
 class BluetoothNotificationController;
+class BluetoothPowerController;
 class BrightnessControlDelegate;
 class CastConfigController;
 class DisplayColorManager;
@@ -479,6 +480,10 @@
     return tray_bluetooth_helper_.get();
   }
 
+  BluetoothPowerController* bluetooth_power_controller() {
+    return bluetooth_power_controller_.get();
+  }
+
   VirtualKeyboardController* virtual_keyboard_controller() {
     return virtual_keyboard_controller_.get();
   }
@@ -778,6 +783,7 @@
       resolution_notification_controller_;
   std::unique_ptr<BluetoothNotificationController>
       bluetooth_notification_controller_;
+  std::unique_ptr<BluetoothPowerController> bluetooth_power_controller_;
   std::unique_ptr<TrayBluetoothHelper> tray_bluetooth_helper_;
   std::unique_ptr<VirtualKeyboardController> virtual_keyboard_controller_;
   std::unique_ptr<chromeos::AudioA11yController> audio_a11y_controller_;
diff --git a/ash/system/bluetooth/bluetooth_power_controller.cc b/ash/system/bluetooth/bluetooth_power_controller.cc
new file mode 100644
index 0000000..cf351ffb
--- /dev/null
+++ b/ash/system/bluetooth/bluetooth_power_controller.cc
@@ -0,0 +1,260 @@
+// 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/bluetooth/bluetooth_power_controller.h"
+
+#include "ash/public/cpp/ash_pref_names.h"
+#include "ash/session/session_controller.h"
+#include "ash/shell.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+
+namespace ash {
+
+BluetoothPowerController::BluetoothPowerController() : weak_ptr_factory_(this) {
+  device::BluetoothAdapterFactory::GetAdapter(
+      base::Bind(&BluetoothPowerController::InitializeOnAdapterReady,
+                 weak_ptr_factory_.GetWeakPtr()));
+  Shell::Get()->AddShellObserver(this);
+  Shell::Get()->session_controller()->AddObserver(this);
+}
+
+BluetoothPowerController::~BluetoothPowerController() {
+  if (bluetooth_adapter_)
+    bluetooth_adapter_->RemoveObserver(this);
+  Shell::Get()->RemoveShellObserver(this);
+  Shell::Get()->session_controller()->RemoveObserver(this);
+}
+
+void BluetoothPowerController::ToggleBluetoothEnabled() {
+  if (active_user_pref_service_) {
+    active_user_pref_service_->SetBoolean(
+        prefs::kUserBluetoothAdapterEnabled,
+        !active_user_pref_service_->GetBoolean(
+            prefs::kUserBluetoothAdapterEnabled));
+  } else if (local_state_pref_service_) {
+    local_state_pref_service_->SetBoolean(
+        prefs::kSystemBluetoothAdapterEnabled,
+        !local_state_pref_service_->GetBoolean(
+            prefs::kSystemBluetoothAdapterEnabled));
+  } else {
+    DLOG(ERROR)
+        << "active user and local state pref service cannot both be null";
+  }
+}
+
+// static
+void BluetoothPowerController::RegisterLocalStatePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kSystemBluetoothAdapterEnabled, false);
+}
+
+// static
+void BluetoothPowerController::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kUserBluetoothAdapterEnabled, false,
+                                PrefRegistry::PUBLIC);
+}
+
+void BluetoothPowerController::StartWatchingActiveUserPrefsChanges() {
+  DCHECK(active_user_pref_service_);
+  DCHECK(Shell::Get()->session_controller()->IsUserPrimary());
+
+  active_user_pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
+  active_user_pref_change_registrar_->Init(active_user_pref_service_);
+  active_user_pref_change_registrar_->Add(
+      prefs::kUserBluetoothAdapterEnabled,
+      base::Bind(
+          &BluetoothPowerController::OnBluetoothPowerActiveUserPrefChanged,
+          base::Unretained(this)));
+}
+
+void BluetoothPowerController::StartWatchingLocalStatePrefsChanges() {
+  DCHECK(local_state_pref_service_);
+
+  local_state_pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
+  local_state_pref_change_registrar_->Init(local_state_pref_service_);
+  local_state_pref_change_registrar_->Add(
+      prefs::kSystemBluetoothAdapterEnabled,
+      base::Bind(
+          &BluetoothPowerController::OnBluetoothPowerLocalStatePrefChanged,
+          base::Unretained(this)));
+}
+
+void BluetoothPowerController::StopWatchingActiveUserPrefsChanges() {
+  active_user_pref_change_registrar_.reset();
+}
+
+void BluetoothPowerController::OnBluetoothPowerActiveUserPrefChanged() {
+  DCHECK(active_user_pref_service_);
+  SetBluetoothPower(active_user_pref_service_->GetBoolean(
+      prefs::kUserBluetoothAdapterEnabled));
+}
+
+void BluetoothPowerController::OnBluetoothPowerLocalStatePrefChanged() {
+  DCHECK(local_state_pref_service_);
+  SetBluetoothPower(local_state_pref_service_->GetBoolean(
+      prefs::kSystemBluetoothAdapterEnabled));
+}
+
+void BluetoothPowerController::SetPrimaryUserBluetoothPowerSetting(
+    bool enabled) {
+  // This method should only be called when the primary user is the active user.
+  CHECK(Shell::Get()->session_controller()->IsUserPrimary());
+
+  active_user_pref_service_->SetBoolean(prefs::kUserBluetoothAdapterEnabled,
+                                        enabled);
+}
+
+void BluetoothPowerController::InitializeOnAdapterReady(
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  bluetooth_adapter_ = std::move(adapter);
+  bluetooth_adapter_->AddObserver(this);
+  if (bluetooth_adapter_->IsPresent()) {
+    RunPendingBluetoothTasks(bluetooth_adapter_.get());
+  }
+}
+
+void BluetoothPowerController::OnActiveUserPrefServiceChanged(
+    PrefService* pref_service) {
+  active_user_pref_service_ = pref_service;
+
+  // Only listen to primary user's pref changes since non-primary users
+  // are not able to change bluetooth pref.
+  if (!Shell::Get()->session_controller()->IsUserPrimary()) {
+    StopWatchingActiveUserPrefsChanges();
+    return;
+  }
+  StartWatchingActiveUserPrefsChanges();
+
+  // Apply the bluetooth pref only for regular users (i.e. users representing
+  // a human individual). We don't want to apply bluetooth pref for other users
+  // e.g. kiosk, guest etc. For non-human users, bluetooth power should be left
+  // to the current power state.
+  if (!is_primary_user_bluetooth_applied_) {
+    ApplyBluetoothPrimaryUserPref();
+    is_primary_user_bluetooth_applied_ = true;
+  }
+}
+
+void BluetoothPowerController::OnLocalStatePrefServiceInitialized(
+    PrefService* pref_service) {
+  // AppLaunchTest.TestQuickLaunch fails under target=linux due to
+  // pref_service being nullptr.
+  if (!pref_service)
+    return;
+
+  local_state_pref_service_ = pref_service;
+
+  StartWatchingLocalStatePrefsChanges();
+
+  if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
+    // Apply the local state pref only if no user has logged in (still in login
+    // screen).
+    ApplyBluetoothLocalStatePref();
+  }
+}
+
+void BluetoothPowerController::AdapterPresentChanged(
+    device::BluetoothAdapter* adapter,
+    bool present) {
+  if (present)
+    RunPendingBluetoothTasks(adapter);
+}
+
+void BluetoothPowerController::ApplyBluetoothPrimaryUserPref() {
+  base::Optional<user_manager::UserType> user_type =
+      Shell::Get()->session_controller()->GetUserType();
+  if (!user_type || !ShouldApplyUserBluetoothSetting(*user_type)) {
+    // Do not apply bluetooth setting if user is not of the allowed types.
+    return;
+  }
+
+  DCHECK(Shell::Get()->session_controller()->IsUserPrimary());
+
+  PrefService* prefs = active_user_pref_service_;
+
+  if (!prefs->FindPreference(prefs::kUserBluetoothAdapterEnabled)
+           ->IsDefaultValue()) {
+    SetBluetoothPower(prefs->GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+    return;
+  }
+
+  // If the user has not had the bluetooth pref yet, set the user pref
+  // according to whatever the current bluetooth power is, except for
+  // new users (first login on the device) always set the new pref to true.
+  if (Shell::Get()->session_controller()->IsUserFirstLogin()) {
+    prefs->SetBoolean(prefs::kUserBluetoothAdapterEnabled, true);
+  } else {
+    SavePrefValue(prefs, prefs::kUserBluetoothAdapterEnabled);
+  }
+}
+
+void BluetoothPowerController::ApplyBluetoothLocalStatePref() {
+  PrefService* prefs = local_state_pref_service_;
+
+  if (prefs->FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+          ->IsDefaultValue()) {
+    // If the device has not had the local state bluetooth pref, set the pref
+    // according to whatever the current bluetooth power is.
+    SavePrefValue(prefs, prefs::kSystemBluetoothAdapterEnabled);
+  } else {
+    SetBluetoothPower(prefs->GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  }
+}
+
+void BluetoothPowerController::SetBluetoothPower(bool enabled) {
+  RunBluetoothTaskWhenAdapterReady(
+      base::BindOnce(&BluetoothPowerController::SetBluetoothPowerOnAdapterReady,
+                     weak_ptr_factory_.GetWeakPtr(), enabled));
+}
+
+void BluetoothPowerController::SetBluetoothPowerOnAdapterReady(
+    bool enabled,
+    device::BluetoothAdapter* adapter) {
+  adapter->SetPowered(enabled, base::Bind(&base::DoNothing),
+                      base::Bind(&base::DoNothing));
+}
+
+void BluetoothPowerController::RunBluetoothTaskWhenAdapterReady(
+    base::OnceCallback<void(device::BluetoothAdapter*)> task) {
+  if (bluetooth_adapter_ && bluetooth_adapter_->IsPresent()) {
+    std::move(task).Run(bluetooth_adapter_.get());
+  } else {
+    pending_bluetooth_tasks_.push_back(std::move(task));
+  }
+}
+
+void BluetoothPowerController::RunPendingBluetoothTasks(
+    device::BluetoothAdapter* adapter) {
+  for (auto& task : pending_bluetooth_tasks_) {
+    std::move(task).Run(adapter);
+  }
+  pending_bluetooth_tasks_.clear();
+}
+
+void BluetoothPowerController::SavePrefValue(PrefService* prefs,
+                                             const char* pref_name) {
+  RunBluetoothTaskWhenAdapterReady(
+      base::BindOnce(&BluetoothPowerController::SavePrefValueOnAdapterReady,
+                     weak_ptr_factory_.GetWeakPtr(), prefs, pref_name));
+}
+
+void BluetoothPowerController::SavePrefValueOnAdapterReady(
+    PrefService* prefs,
+    const char* pref_name,
+    device::BluetoothAdapter* adapter) {
+  prefs->SetBoolean(pref_name, adapter->IsPowered());
+}
+
+bool BluetoothPowerController::ShouldApplyUserBluetoothSetting(
+    user_manager::UserType user_type) const {
+  return user_type == user_manager::USER_TYPE_REGULAR ||
+         user_type == user_manager::USER_TYPE_CHILD ||
+         user_type == user_manager::USER_TYPE_SUPERVISED ||
+         user_type == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
+}
+
+}  // namespace ash
diff --git a/ash/system/bluetooth/bluetooth_power_controller.h b/ash/system/bluetooth/bluetooth_power_controller.h
new file mode 100644
index 0000000..f97d0613
--- /dev/null
+++ b/ash/system/bluetooth/bluetooth_power_controller.h
@@ -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.
+
+#ifndef ASH_SYSTEM_BLUETOOTH_BLUETOOTH_POWER_CONTROLLER_H_
+#define ASH_SYSTEM_BLUETOOTH_BLUETOOTH_POWER_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "ash/session/session_observer.h"
+#include "ash/shell_observer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/user_manager/user_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ash {
+
+// Listens to changes of bluetooth power preferences and apply them to the
+// device. Also initializes the bluetooth power during system startup
+// and user session startup.
+//
+// This should be the only entity controlling bluetooth power. All other code
+// outside ash that wants to control bluetooth power should set user pref
+// setting instead.
+class ASH_EXPORT BluetoothPowerController
+    : public SessionObserver,
+      public ShellObserver,
+      public device::BluetoothAdapter::Observer {
+ public:
+  BluetoothPowerController();
+  ~BluetoothPowerController() override;
+
+  // Toggles the bluetooth power setting on or off.
+  void ToggleBluetoothEnabled();
+
+  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  // Sets the primary user's bluetooth power pref. Setting the pref will also
+  // trigger the change of the bluetooth power. This method can only be called
+  // when the primary user is the active user, otherwise the operation is
+  // ignored.
+  void SetPrimaryUserBluetoothPowerSetting(bool enabled);
+
+  // Called when BluetoothAdapterFactory::GetAdapter is ready to pass the
+  // adapter pointer.
+  void InitializeOnAdapterReady(
+      scoped_refptr<device::BluetoothAdapter> adapter);
+
+  // SessionObserver:
+  void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
+
+  // ShellObserver:
+  void OnLocalStatePrefServiceInitialized(PrefService* pref_service) override;
+
+  // BluetoothAdapter::Observer:
+  void AdapterPresentChanged(device::BluetoothAdapter* adapter,
+                             bool present) override;
+
+ private:
+  friend class BluetoothPowerControllerTest;
+
+  void StartWatchingActiveUserPrefsChanges();
+  void StartWatchingLocalStatePrefsChanges();
+  void StopWatchingActiveUserPrefsChanges();
+
+  void OnBluetoothPowerActiveUserPrefChanged();
+  void OnBluetoothPowerLocalStatePrefChanged();
+
+  // At primary user session startup, apply the user's bluetooth power setting
+  // or set the default if the user doesn't have the setting yet.
+  void ApplyBluetoothPrimaryUserPref();
+
+  // At login screen startup, apply the local state bluetooth power setting
+  // or set the default if the device doesn't have the setting yet.
+  void ApplyBluetoothLocalStatePref();
+
+  // Sets the bluetooth power, may defer the operation if bluetooth adapter
+  // is not yet ready.
+  void SetBluetoothPower(bool enabled);
+
+  // Sets the bluetooth power given the ready adapter.
+  void SetBluetoothPowerOnAdapterReady(bool enabled,
+                                       device::BluetoothAdapter* adapter);
+
+  // If adapter is ready run the task right now, otherwise add the task
+  // to the queue which will be executed when bluetooth adapter is ready.
+  void RunBluetoothTaskWhenAdapterReady(
+      base::OnceCallback<void(device::BluetoothAdapter*)> task);
+
+  // Runs all pending bluetooth adapter-dependent tasks.
+  void RunPendingBluetoothTasks(device::BluetoothAdapter* adapter);
+
+  // Sets the pref value based on current bluetooth power state. May defer
+  // the operation if bluetooth adapter is not ready yet.
+  void SavePrefValue(PrefService* prefs, const char* pref_name);
+
+  // Sets the pref value based on current bluetooth power state, given the ready
+  // bluetooth adapter.
+  void SavePrefValueOnAdapterReady(PrefService* prefs,
+                                   const char* pref_name,
+                                   device::BluetoothAdapter* adapter);
+
+  // Decides whether to apply bluetooth setting based on user type.
+  // Returns true if the user type represents a human individual, currently this
+  // includes: regular, child, supervised, or active directory. The other types
+  // do not represent human account so those account should follow system-wide
+  // bluetooth setting instead.
+  bool ShouldApplyUserBluetoothSetting(user_manager::UserType user_type) const;
+
+  // Remembers whether we have ever applied the primary user's bluetooth
+  // setting. If this variable is true, we will ignore any active user change
+  // event since we know that the primary user's bluetooth setting has been
+  // applied.
+  bool is_primary_user_bluetooth_applied_ = false;
+
+  PrefService* active_user_pref_service_ = nullptr;
+  PrefService* local_state_pref_service_ = nullptr;
+
+  // Contains pending tasks which depend on the availability of bluetooth
+  // adapter.
+  std::vector<base::OnceCallback<void(device::BluetoothAdapter*)>>
+      pending_bluetooth_tasks_;
+
+  // The registrar used to watch prefs changes in the above
+  // |active_user_pref_service_| from outside ash.
+  // NOTE: Prefs are how Chrome communicates changes to the bluetooth power
+  // settings controlled by this class from the WebUI settings.
+  std::unique_ptr<PrefChangeRegistrar> active_user_pref_change_registrar_;
+  std::unique_ptr<PrefChangeRegistrar> local_state_pref_change_registrar_;
+
+  scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
+
+  base::WeakPtrFactory<BluetoothPowerController> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothPowerController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_BLUETOOTH_BLUETOOTH_POWER_CONTROLLER_H_
diff --git a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
new file mode 100644
index 0000000..9ea3f063
--- /dev/null
+++ b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
@@ -0,0 +1,300 @@
+// 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/bluetooth/bluetooth_power_controller.h"
+
+#include "ash/public/cpp/ash_pref_names.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 "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
+
+namespace ash {
+namespace {
+
+constexpr char kUser1Email[] = "user1@bluetooth";
+constexpr bool kUserFirstLogin = true;
+
+}  // namespace
+
+class BluetoothPowerControllerTest : public NoSessionAshTestBase {
+ public:
+  BluetoothPowerControllerTest() {}
+  ~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(
+        local_state_prefs_.registry());
+
+    GetController()->local_state_pref_service_ = &local_state_prefs_;
+
+    // Makes sure we get the callback from BluetoothAdapterFactory::GetAdapter
+    // first before running the remaining test.
+    RunAllPendingInMessageLoop();
+  }
+
+  void AddUserSessionAndStartWatchingPrefsChanges(
+      const std::string& display_email,
+      user_manager::UserType user_type = user_manager::USER_TYPE_REGULAR,
+      bool is_new_profile = false) {
+    GetSessionControllerClient()->AddUserSession(
+        display_email, user_type, false /* enable_settings */,
+        false /* provide_pref_service */, is_new_profile);
+    GetController()->active_user_pref_service_ = &active_user_prefs_;
+    GetController()->StartWatchingActiveUserPrefsChanges();
+  }
+
+  void StartWatchingLocalStatePrefsChanges() {
+    GetController()->StartWatchingLocalStatePrefsChanges();
+  }
+
+ 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();
+  }
+
+  void ApplyBluetoothPrimaryUserPref() {
+    Shell::Get()->bluetooth_power_controller()->ApplyBluetoothPrimaryUserPref();
+  }
+
+  TestingPrefServiceSimple active_user_prefs_;
+  TestingPrefServiceSimple local_state_prefs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BluetoothPowerControllerTest);
+};
+
+// Tests toggling Bluetooth setting on and off.
+TEST_F(BluetoothPowerControllerTest, ToggleBluetoothEnabled) {
+  // Toggling bluetooth on/off when there is no user session should affect
+  // local state prefs.
+  EXPECT_FALSE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_TRUE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_FALSE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+
+  // Toggling bluetooth on/off when there is user session should affect
+  // user prefs.
+  AddUserSessionAndStartWatchingPrefsChanges(kUser1Email);
+  EXPECT_FALSE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_TRUE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_FALSE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+}
+
+// Tests that BluetoothPowerController listens to local state pref changes
+// and applies the changes to bluetooth device.
+TEST_F(BluetoothPowerControllerTest, ListensPrefChangesLocalState) {
+  StartWatchingLocalStatePrefsChanges();
+
+  // Makes sure we start with bluetooth power off.
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+  EXPECT_FALSE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+
+  // Power should be turned on when pref changes to enabled.
+  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+
+  // Power should be turned off when pref changes to disabled.
+  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, false);
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+}
+
+// Tests that BluetoothPowerController listens to active user pref changes
+// and applies the changes to bluetooth device.
+TEST_F(BluetoothPowerControllerTest, ListensPrefChangesActiveUser) {
+  AddUserSessionAndStartWatchingPrefsChanges(kUser1Email);
+
+  // Makes sure we start with bluetooth power off.
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+  EXPECT_FALSE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+
+  // Power should be turned on when pref changes to enabled.
+  active_user_prefs_.SetBoolean(prefs::kUserBluetoothAdapterEnabled, true);
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+
+  // Power should be turned off when pref changes to disabled.
+  active_user_prefs_.SetBoolean(prefs::kUserBluetoothAdapterEnabled, false);
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+}
+
+// Tests how BluetoothPowerController applies the local state pref when
+// the pref hasn't been set before.
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothLocalStatePrefDefault) {
+  // Makes sure pref hasn't been set before.
+  EXPECT_TRUE(
+      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power on.
+  GetBluetoothAdapter()->SetPowered(true, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothLocalStatePref();
+
+  // Pref should now contain the current bluetooth adapter state (on).
+  EXPECT_FALSE(
+      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  EXPECT_TRUE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+}
+
+// Tests how BluetoothPowerController applies the local state pref when
+// the pref has been set before.
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothLocalStatePrefOn) {
+  // Set the pref to true.
+  local_state_prefs_.SetBoolean(prefs::kSystemBluetoothAdapterEnabled, true);
+  EXPECT_FALSE(
+      local_state_prefs_.FindPreference(prefs::kSystemBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power off.
+  GetBluetoothAdapter()->SetPowered(false, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothLocalStatePref();
+
+  // Bluetooth power setting should be applied (on), and pref value unchanged.
+  EXPECT_TRUE(
+      local_state_prefs_.GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+}
+
+// Tests how BluetoothPowerController applies the user pref when
+// the pref hasn't been set before.
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothPrimaryUserPrefDefault) {
+  AddUserSessionAndStartWatchingPrefsChanges(kUser1Email);
+
+  // Makes sure pref hasn't been set before.
+  EXPECT_TRUE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power off.
+  GetBluetoothAdapter()->SetPowered(false, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothPrimaryUserPref();
+
+  // Pref should now contain the current bluetooth adapter state (off).
+  EXPECT_FALSE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  EXPECT_FALSE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+}
+
+// Tests how BluetoothPowerController applies the user pref when
+// the pref hasn't been set before, and it's a first-login user.
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothPrimaryUserPrefDefaultNew) {
+  AddUserSessionAndStartWatchingPrefsChanges(
+      kUser1Email, user_manager::USER_TYPE_REGULAR, kUserFirstLogin);
+
+  // Makes sure pref hasn't been set before.
+  EXPECT_TRUE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power off.
+  GetBluetoothAdapter()->SetPowered(false, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothPrimaryUserPref();
+
+  // Pref should be set to true for first-login users, and this will also
+  // trigger the bluetooth power on.
+  EXPECT_FALSE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  EXPECT_TRUE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+}
+
+// Tests how BluetoothPowerController applies the user pref when
+// the pref hasn't been set before, but not a regular user (e.g. kiosk).
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothKioskUserPrefDefault) {
+  AddUserSessionAndStartWatchingPrefsChanges(kUser1Email,
+                                             user_manager::USER_TYPE_KIOSK_APP);
+
+  // Makes sure pref hasn't been set before.
+  EXPECT_TRUE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power off.
+  GetBluetoothAdapter()->SetPowered(false, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothPrimaryUserPref();
+
+  // For non-regular user, do not apply the bluetooth setting and no need
+  // to set the pref.
+  EXPECT_TRUE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  EXPECT_FALSE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+}
+
+// Tests how BluetoothPowerController applies the user pref when
+// the pref has been set before.
+TEST_F(BluetoothPowerControllerTest, ApplyBluetoothPrimaryUserPrefOn) {
+  AddUserSessionAndStartWatchingPrefsChanges(kUser1Email);
+
+  // Set the pref to true.
+  active_user_prefs_.SetBoolean(prefs::kUserBluetoothAdapterEnabled, true);
+  EXPECT_FALSE(
+      active_user_prefs_.FindPreference(prefs::kUserBluetoothAdapterEnabled)
+          ->IsDefaultValue());
+  // Start with bluetooth power off.
+  GetBluetoothAdapter()->SetPowered(false, base::Bind(&base::DoNothing),
+                                    base::Bind(&base::DoNothing));
+  EXPECT_FALSE(GetBluetoothAdapter()->IsPowered());
+
+  ApplyBluetoothPrimaryUserPref();
+
+  // Pref should be applied to trigger the bluetooth power on, and the pref
+  // value should be unchanged..
+  EXPECT_TRUE(
+      active_user_prefs_.GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
+}
+
+}  // namespace ash
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index d6377f4..c5427b9 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -12,8 +12,10 @@
 #include "ash/ash_view_ids.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/bluetooth/bluetooth_power_controller.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/system_tray.h"
@@ -390,10 +392,12 @@
                            const ui::Event& event) override {
     if (sender == toggle_) {
       TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
+      BluetoothPowerController* power_controller =
+          Shell::Get()->bluetooth_power_controller();
       Shell::Get()->metrics()->RecordUserMetricsAction(
           helper->GetBluetoothEnabled() ? UMA_STATUS_AREA_BLUETOOTH_DISABLED
                                         : UMA_STATUS_AREA_BLUETOOTH_ENABLED);
-      helper->ToggleBluetoothEnabled();
+      power_controller->ToggleBluetoothEnabled();
     } else if (sender == settings_) {
       ShowSettings();
     } else {
@@ -538,8 +542,20 @@
 
 views::View* TrayBluetooth::CreateDefaultView(LoginStatus status) {
   CHECK(default_ == nullptr);
+  SessionController* session_controller = Shell::Get()->session_controller();
   default_ = new tray::BluetoothDefaultView(this);
-  default_->SetEnabled(status != LoginStatus::LOCKED);
+  if (!session_controller->IsActiveUserSessionStarted()) {
+    // Bluetooth power setting is always mutable in login screen before any
+    // user logs in. The changes will affect local state preferences.
+    default_->SetEnabled(true);
+  } else {
+    // The bluetooth setting should be mutable only if:
+    // * the active user is the primary user, and
+    // * the session is not in lock screen
+    // The changes will affect the primary user's preferences.
+    default_->SetEnabled(session_controller->IsUserPrimary() &&
+                         status != LoginStatus::LOCKED);
+  }
   default_->Update();
   return default_;
 }
diff --git a/ash/system/bluetooth/tray_bluetooth_helper.cc b/ash/system/bluetooth/tray_bluetooth_helper.cc
index 2ea5c3f7..fa4b461 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper.cc
@@ -119,11 +119,6 @@
       device->IsConnected());
 }
 
-void TrayBluetoothHelper::ToggleBluetoothEnabled() {
-  adapter_->SetPowered(!adapter_->IsPowered(), base::Bind(&base::DoNothing),
-                       base::Bind(&base::DoNothing));
-}
-
 bool TrayBluetoothHelper::GetBluetoothAvailable() {
   return adapter_ && adapter_->IsPresent();
 }
diff --git a/ash/system/bluetooth/tray_bluetooth_helper.h b/ash/system/bluetooth/tray_bluetooth_helper.h
index 7aa22d59..4560614 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper.h
+++ b/ash/system/bluetooth/tray_bluetooth_helper.h
@@ -67,9 +67,6 @@
   // Connect to a specific bluetooth device.
   void ConnectToBluetoothDevice(const std::string& address);
 
-  // Toggles whether bluetooth is enabled.
-  void ToggleBluetoothEnabled();
-
   // Returns whether bluetooth capability is available (e.g. the device has
   // hardware support).
   bool GetBluetoothAvailable();
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_unittest.cc b/ash/system/bluetooth/tray_bluetooth_helper_unittest.cc
index d88b11f..396b3cc 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_unittest.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_unittest.cc
@@ -18,7 +18,7 @@
 
 using TrayBluetoothHelperTest = AshTestBase;
 
-// Tests basic functionality like turning Bluetooth on and off.
+// Tests basic functionality.
 TEST_F(TrayBluetoothHelperTest, Basics) {
   // Set Bluetooth discovery simulation delay to 0 so the test doesn't have to
   // wait or use timers.
@@ -38,11 +38,6 @@
   // The devices are fake in tests, so don't assume any particular number.
   EXPECT_FALSE(devices.empty());
 
-  // Turn Bluetooth on.
-  helper.ToggleBluetoothEnabled();
-  RunAllPendingInMessageLoop();
-  EXPECT_TRUE(helper.GetBluetoothEnabled());
-
   helper.StartBluetoothDiscovering();
   RunAllPendingInMessageLoop();
   EXPECT_TRUE(helper.HasBluetoothDiscoverySession());
@@ -50,11 +45,6 @@
   helper.StopBluetoothDiscovering();
   RunAllPendingInMessageLoop();
   EXPECT_FALSE(helper.HasBluetoothDiscoverySession());
-
-  // Turn Bluetooth off.
-  helper.ToggleBluetoothEnabled();
-  RunAllPendingInMessageLoop();
-  EXPECT_FALSE(helper.GetBluetoothEnabled());
 }
 
 }  // namespace
diff --git a/ash/wallpaper/wallpaper_controller_test_api.cc b/ash/wallpaper/wallpaper_controller_test_api.cc
index 95fcd00..2b74f87e 100644
--- a/ash/wallpaper/wallpaper_controller_test_api.cc
+++ b/ash/wallpaper/wallpaper_controller_test_api.cc
@@ -5,6 +5,7 @@
 #include "ash/wallpaper/wallpaper_controller_test_api.h"
 #include "ash/wallpaper/wallpaper_controller.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -18,7 +19,7 @@
 
 SkColor WallpaperControllerTestApi::ApplyColorProducingWallpaper() {
   const SkColor color = SkColorSetRGB(60, 40, 40);
-  const SkColor expected_color = SkColorSetRGB(30, 20, 20);
+  const SkColor expected_color = SkColorSetRGB(18, 12, 12);
 
   gfx::Canvas canvas(gfx::Size(5, 5), 1.0f, true);
   canvas.DrawColor(color);
diff --git a/base/bind.h b/base/bind.h
index b1c6ac698..32a1c851e 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -66,18 +66,18 @@
 };
 
 // The implementation of TransformToUnwrappedType below.
-template <RepeatMode, typename T>
+template <bool is_once, typename T>
 struct TransformToUnwrappedTypeImpl;
 
 template <typename T>
-struct TransformToUnwrappedTypeImpl<RepeatMode::Once, T> {
+struct TransformToUnwrappedTypeImpl<true, T> {
   using StoredType = std::decay_t<T>;
   using ForwardType = StoredType&&;
   using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
 };
 
 template <typename T>
-struct TransformToUnwrappedTypeImpl<RepeatMode::Repeating, T> {
+struct TransformToUnwrappedTypeImpl<false, T> {
   using StoredType = std::decay_t<T>;
   using ForwardType = const StoredType&;
   using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
@@ -85,40 +85,40 @@
 
 // Transform |T| into `Unwrapped` type, which is passed to the target function.
 // Example:
-//   In repeat_mode == RepeatMode::Once case,
+//   In is_once == true case,
 //     `int&&` -> `int&&`,
 //     `const int&` -> `int&&`,
 //     `OwnedWrapper<int>&` -> `int*&&`.
-//   In repeat_mode == RepeatMode::Repeating case,
+//   In is_once == false case,
 //     `int&&` -> `const int&`,
 //     `const int&` -> `const int&`,
 //     `OwnedWrapper<int>&` -> `int* const &`.
-template <RepeatMode repeat_mode, typename T>
+template <bool is_once, typename T>
 using TransformToUnwrappedType =
-    typename TransformToUnwrappedTypeImpl<repeat_mode, T>::Unwrapped;
+    typename TransformToUnwrappedTypeImpl<is_once, T>::Unwrapped;
 
 // Transforms |Args| into `Unwrapped` types, and packs them into a TypeList.
 // If |is_method| is true, tries to dereference the first argument to support
 // smart pointers.
-template <RepeatMode repeat_mode, bool is_method, typename... Args>
+template <bool is_once, bool is_method, typename... Args>
 struct MakeUnwrappedTypeListImpl {
-  using Type = TypeList<TransformToUnwrappedType<repeat_mode, Args>...>;
+  using Type = TypeList<TransformToUnwrappedType<is_once, Args>...>;
 };
 
 // Performs special handling for this pointers.
 // Example:
 //   int* -> int*,
 //   std::unique_ptr<int> -> int*.
-template <RepeatMode repeat_mode, typename Receiver, typename... Args>
-struct MakeUnwrappedTypeListImpl<repeat_mode, true, Receiver, Args...> {
-  using UnwrappedReceiver = TransformToUnwrappedType<repeat_mode, Receiver>;
+template <bool is_once, typename Receiver, typename... Args>
+struct MakeUnwrappedTypeListImpl<is_once, true, Receiver, Args...> {
+  using UnwrappedReceiver = TransformToUnwrappedType<is_once, Receiver>;
   using Type = TypeList<decltype(&*std::declval<UnwrappedReceiver>()),
-                        TransformToUnwrappedType<repeat_mode, Args>...>;
+                        TransformToUnwrappedType<is_once, Args>...>;
 };
 
-template <RepeatMode repeat_mode, bool is_method, typename... Args>
+template <bool is_once, bool is_method, typename... Args>
 using MakeUnwrappedTypeList =
-    typename MakeUnwrappedTypeListImpl<repeat_mode, is_method, Args...>::Type;
+    typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
 
 }  // namespace internal
 
@@ -139,8 +139,8 @@
   using FunctorTraits = typename Helper::FunctorTraits;
   using BoundArgsList = typename Helper::BoundArgsList;
   using UnwrappedArgsList =
-      internal::MakeUnwrappedTypeList<internal::RepeatMode::Once,
-                                      FunctorTraits::is_method, Args&&...>;
+      internal::MakeUnwrappedTypeList<true, FunctorTraits::is_method,
+                                      Args&&...>;
   using BoundParamsList = typename Helper::BoundParamsList;
   static_assert(internal::AssertBindArgsValidity<
                     std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
@@ -180,8 +180,8 @@
   using FunctorTraits = typename Helper::FunctorTraits;
   using BoundArgsList = typename Helper::BoundArgsList;
   using UnwrappedArgsList =
-      internal::MakeUnwrappedTypeList<internal::RepeatMode::Repeating,
-                                      FunctorTraits::is_method, Args&&...>;
+      internal::MakeUnwrappedTypeList<false, FunctorTraits::is_method,
+                                      Args&&...>;
   using BoundParamsList = typename Helper::BoundParamsList;
   static_assert(internal::AssertBindArgsValidity<
                     std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
diff --git a/base/bind_helpers.h b/base/bind_helpers.h
index 8e1c725..638cedd 100644
--- a/base/bind_helpers.h
+++ b/base/bind_helpers.h
@@ -551,11 +551,19 @@
 };
 
 // Specialization for a nested bind.
-template <typename Signature,
-          typename... BoundArgs,
-          internal::CopyMode copy_mode,
-          internal::RepeatMode repeat_mode>
-struct CallbackCancellationTraits<Callback<Signature, copy_mode, repeat_mode>,
+template <typename Signature, typename... BoundArgs>
+struct CallbackCancellationTraits<OnceCallback<Signature>,
+                                  std::tuple<BoundArgs...>> {
+  static constexpr bool is_cancellable = true;
+
+  template <typename Functor>
+  static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
+    return functor.IsCancelled();
+  }
+};
+
+template <typename Signature, typename... BoundArgs>
+struct CallbackCancellationTraits<RepeatingCallback<Signature>,
                                   std::tuple<BoundArgs...>> {
   static constexpr bool is_cancellable = true;
 
diff --git a/base/bind_internal.h b/base/bind_internal.h
index 756f890..c19f759 100644
--- a/base/bind_internal.h
+++ b/base/bind_internal.h
@@ -225,10 +225,24 @@
   }
 };
 
-// For Callbacks.
-template <typename R, typename... Args,
-          CopyMode copy_mode, RepeatMode repeat_mode>
-struct FunctorTraits<Callback<R(Args...), copy_mode, repeat_mode>> {
+// For OnceCallbacks.
+template <typename R, typename... Args>
+struct FunctorTraits<OnceCallback<R(Args...)>> {
+  using RunType = R(Args...);
+  static constexpr bool is_method = false;
+  static constexpr bool is_nullable = true;
+
+  template <typename CallbackType, typename... RunArgs>
+  static R Invoke(CallbackType&& callback, RunArgs&&... args) {
+    DCHECK(!callback.is_null());
+    return std::forward<CallbackType>(callback).Run(
+        std::forward<RunArgs>(args)...);
+  }
+};
+
+// For RepeatingCallbacks.
+template <typename R, typename... Args>
+struct FunctorTraits<RepeatingCallback<R(Args...)>> {
   using RunType = R(Args...);
   static constexpr bool is_method = false;
   static constexpr bool is_nullable = true;
diff --git a/base/bind_unittest.nc b/base/bind_unittest.nc
index cfdfe98..92b461f 100644
--- a/base/bind_unittest.nc
+++ b/base/bind_unittest.nc
@@ -70,7 +70,7 @@
 void VoidPolymorphic1(T t) {
 }
 
-#if defined(NCTEST_METHOD_ON_CONST_OBJECT)  // [r"fatal error: static_assert failed \"|Param| needs to be constructible from |Unwrapped| type\.\""]
+#if defined(NCTEST_METHOD_ON_CONST_OBJECT)  // [r"fatal error: static_assert failed \"\|Param\| needs to be constructible from \|Unwrapped\| type\."]
 
 // Method bound to const-object.
 //
@@ -107,7 +107,7 @@
   no_ref_const_cb.Run();
 }
 
-#elif defined(NCTEST_CONST_POINTER)  // [r"fatal error: static_assert failed \"|Param| needs to be constructible from |Unwrapped| type\.\""]
+#elif defined(NCTEST_CONST_POINTER)  // [r"fatal error: static_assert failed \"\|Param\| needs to be constructible from \|Unwrapped\| type\."]
 
 // Const argument used with non-const pointer parameter of same type.
 //
@@ -119,7 +119,7 @@
   pointer_same_cb.Run();
 }
 
-#elif defined(NCTEST_CONST_POINTER_SUBTYPE)  // [r"fatal error: static_assert failed \"|Param| needs to be constructible from |Unwrapped| type\.\""]
+#elif defined(NCTEST_CONST_POINTER_SUBTYPE)  // [r"fatal error: static_assert failed \"\|Param\| needs to be constructible from \|Unwrapped\| type\."]
 
 // Const argument used with non-const pointer parameter of super type.
 //
@@ -148,7 +148,7 @@
   ref_arg_cb.Run(p);
 }
 
-#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM)  // [r"fatal error: static_assert failed \"|Param| needs to be constructible from |Unwrapped| type\.\""]
+#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM)  // [r"fatal error: static_assert failed \"\|Param\| needs to be constructible from \|Unwrapped\| type\."]
 
 // Binding functions with reference parameters, unsupported.
 //
@@ -159,7 +159,7 @@
   ref_cb.Run();
 }
 
-#elif defined(NCTEST_NO_IMPLICIT_ARRAY_PTR_CONVERSION)  // [r"fatal error: static_assert failed \"First bound argument to a method cannot be an array.\""]
+#elif defined(NCTEST_NO_IMPLICIT_ARRAY_PTR_CONVERSION)  // [r"fatal error: static_assert failed \"First bound argument to a method cannot be an array\.\""]
 
 // A method should not be bindable with an array of objects.
 //
@@ -173,7 +173,7 @@
   method_bound_to_array_cb.Run();
 }
 
-#elif defined(NCTEST_NO_RVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr.\""]
+#elif defined(NCTEST_NO_RVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr\.\""]
 
 // Refcounted types should not be bound as a raw pointer.
 void WontCompile() {
@@ -185,7 +185,7 @@
       Bind(&VoidPolymorphic1<HasRef*>, &for_raw_ptr);
 }
 
-#elif defined(NCTEST_NO_LVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr.\""]
+#elif defined(NCTEST_NO_LVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr\.\""]
 
 // Refcounted types should not be bound as a raw pointer.
 void WontCompile() {
@@ -194,7 +194,7 @@
       Bind(&VoidPolymorphic1<HasRef*>, for_raw_ptr);
 }
 
-#elif defined(NCTEST_NO_RVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr.\""]
+#elif defined(NCTEST_NO_RVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr\.\""]
 
 // Refcounted types should not be bound as a raw pointer.
 void WontCompile() {
@@ -203,7 +203,7 @@
       Bind(&VoidPolymorphic1<const HasRef*>, &for_raw_ptr);
 }
 
-#elif defined(NCTEST_NO_LVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr.\""]
+#elif defined(NCTEST_NO_LVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed \"A parameter is a refcounted type and needs scoped_refptr\.\""]
 
 // Refcounted types should not be bound as a raw pointer.
 void WontCompile() {
diff --git a/base/callback.h b/base/callback.h
index d2a6228..043f72d 100644
--- a/base/callback.h
+++ b/base/callback.h
@@ -19,62 +19,74 @@
 
 namespace base {
 
-namespace internal {
-
-template <typename From, typename To>
-struct IsCallbackConvertible : std::false_type {};
-
-template <typename Signature>
-struct IsCallbackConvertible<RepeatingCallback<Signature>,
-                             OnceCallback<Signature>> : std::true_type {};
-
-}  // namespace internal
-
-template <typename R,
-          typename... Args,
-          internal::CopyMode copy_mode,
-          internal::RepeatMode repeat_mode>
-class Callback<R(Args...), copy_mode, repeat_mode>
-    : public internal::CallbackBase<copy_mode> {
+template <typename R, typename... Args>
+class OnceCallback<R(Args...)> : public internal::CallbackBase {
  public:
-  static_assert(repeat_mode != internal::RepeatMode::Once ||
-                copy_mode == internal::CopyMode::MoveOnly,
-                "OnceCallback must be MoveOnly.");
-
   using RunType = R(Args...);
   using PolymorphicInvoke = R (*)(internal::BindStateBase*, Args&&...);
 
-  Callback() : internal::CallbackBase<copy_mode>(nullptr) {}
+  OnceCallback() : internal::CallbackBase(nullptr) {}
 
-  explicit Callback(internal::BindStateBase* bind_state)
-      : internal::CallbackBase<copy_mode>(bind_state) {
-  }
+  explicit OnceCallback(internal::BindStateBase* bind_state)
+      : internal::CallbackBase(bind_state) {}
 
-  template <
-      typename OtherCallback,
-      typename = std::enable_if_t<
-          internal::IsCallbackConvertible<OtherCallback, Callback>::value>>
-  Callback(OtherCallback other)
-      : internal::CallbackBase<copy_mode>(std::move(other)) {}
+  OnceCallback(const OnceCallback&) = delete;
+  OnceCallback& operator=(const OnceCallback&) = delete;
 
-  template <
-      typename OtherCallback,
-      typename = std::enable_if_t<
-          internal::IsCallbackConvertible<OtherCallback, Callback>::value>>
-  Callback& operator=(OtherCallback other) {
-    static_cast<internal::CallbackBase<copy_mode>&>(*this) = std::move(other);
+  OnceCallback(OnceCallback&&) = default;
+  OnceCallback& operator=(OnceCallback&&) = default;
+
+  OnceCallback(RepeatingCallback<RunType> other)
+      : internal::CallbackBase(std::move(other)) {}
+
+  OnceCallback& operator=(RepeatingCallback<RunType> other) {
+    static_cast<internal::CallbackBase&>(*this) = std::move(other);
     return *this;
   }
 
-  bool Equals(const Callback& other) const {
-    return this->EqualsInternal(other);
+  bool Equals(const OnceCallback& other) const { return EqualsInternal(other); }
+
+  R Run(Args... args) const & {
+    static_assert(!sizeof(*this),
+                  "OnceCallback::Run() may only be invoked on a non-const "
+                  "rvalue, i.e. std::move(callback).Run().");
+    NOTREACHED();
+  }
+
+  R Run(Args... args) && {
+    // Move the callback instance into a local variable before the invocation,
+    // that ensures the internal state is cleared after the invocation.
+    // It's not safe to touch |this| after the invocation, since running the
+    // bound function may destroy |this|.
+    OnceCallback cb = std::move(*this);
+    PolymorphicInvoke f =
+        reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
+    return f(cb.bind_state_.get(), std::forward<Args>(args)...);
+  }
+};
+
+template <typename R, typename... Args>
+class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
+ public:
+  using RunType = R(Args...);
+  using PolymorphicInvoke = R (*)(internal::BindStateBase*, Args&&...);
+
+  RepeatingCallback() : internal::CallbackBaseCopyable(nullptr) {}
+
+  explicit RepeatingCallback(internal::BindStateBase* bind_state)
+      : internal::CallbackBaseCopyable(bind_state) {}
+
+  // Copyable and movabl.
+  RepeatingCallback(const RepeatingCallback&) = default;
+  RepeatingCallback& operator=(const RepeatingCallback&) = default;
+  RepeatingCallback(RepeatingCallback&&) = default;
+  RepeatingCallback& operator=(RepeatingCallback&&) = default;
+
+  bool Equals(const RepeatingCallback& other) const {
+    return EqualsInternal(other);
   }
 
   R Run(Args... args) const & {
-    static_assert(repeat_mode == internal::RepeatMode::Repeating,
-                  "OnceCallback::Run() may only be invoked on a non-const "
-                  "rvalue, i.e. std::move(callback).Run().");
-
     PolymorphicInvoke f =
         reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
     return f(this->bind_state_.get(), std::forward<Args>(args)...);
@@ -85,7 +97,7 @@
     // that ensures the internal state is cleared after the invocation.
     // It's not safe to touch |this| after the invocation, since running the
     // bound function may destroy |this|.
-    Callback cb = std::move(*this);
+    RepeatingCallback cb = std::move(*this);
     PolymorphicInvoke f =
         reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
     return f(cb.bind_state_.get(), std::forward<Args>(args)...);
diff --git a/base/callback_forward.h b/base/callback_forward.h
index 13eed0e..f1851c4 100644
--- a/base/callback_forward.h
+++ b/base/callback_forward.h
@@ -6,42 +6,21 @@
 #define BASE_CALLBACK_FORWARD_H_
 
 namespace base {
-namespace internal {
 
-// CopyMode is used to control the copyablity of a Callback.
-// MoveOnly indicates the Callback is not copyable but movable, and Copyable
-// indicates it is copyable and movable.
-enum class CopyMode {
-  MoveOnly,
-  Copyable,
-};
+template <typename Signature>
+class OnceCallback;
 
-enum class RepeatMode {
-  Once,
-  Repeating,
-};
+template <typename Signature>
+class RepeatingCallback;
 
-}  // namespace internal
-
-template <typename Signature,
-          internal::CopyMode copy_mode = internal::CopyMode::Copyable,
-          internal::RepeatMode repeat_mode = internal::RepeatMode::Repeating>
-class Callback;
+template <typename Signature>
+using Callback = RepeatingCallback<Signature>;
 
 // Syntactic sugar to make Callback<void()> easier to declare since it
 // will be used in a lot of APIs with delayed execution.
-using Closure = Callback<void()>;
-
-template <typename Signature>
-using OnceCallback = Callback<Signature,
-                              internal::CopyMode::MoveOnly,
-                              internal::RepeatMode::Once>;
-template <typename Signature>
-using RepeatingCallback = Callback<Signature,
-                                   internal::CopyMode::Copyable,
-                                   internal::RepeatMode::Repeating>;
 using OnceClosure = OnceCallback<void()>;
 using RepeatingClosure = RepeatingCallback<void()>;
+using Closure = Callback<void()>;
 
 }  // namespace base
 
diff --git a/base/callback_helpers.h b/base/callback_helpers.h
index 0cba0fd..2a9f7f4 100644
--- a/base/callback_helpers.h
+++ b/base/callback_helpers.h
@@ -25,12 +25,9 @@
 
 namespace base {
 
-template <typename Signature,
-          internal::CopyMode copy_mode,
-          internal::RepeatMode repeat_mode>
-Callback<Signature, copy_mode, repeat_mode> ResetAndReturn(
-    Callback<Signature, copy_mode, repeat_mode>* cb) {
-  Callback<Signature, copy_mode, repeat_mode> ret(std::move(*cb));
+template <typename CallbackType>
+CallbackType ResetAndReturn(CallbackType* cb) {
+  CallbackType ret(std::move(*cb));
   DCHECK(!*cb);
   return ret;
 }
diff --git a/base/callback_internal.cc b/base/callback_internal.cc
index a760f06..864c1a0 100644
--- a/base/callback_internal.cc
+++ b/base/callback_internal.cc
@@ -33,73 +33,61 @@
       destructor_(destructor),
       is_cancelled_(is_cancelled) {}
 
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(CallbackBase&& c) = default;
-
-CallbackBase<CopyMode::MoveOnly>&
-CallbackBase<CopyMode::MoveOnly>::operator=(CallbackBase&& c) = default;
-
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(
-    const CallbackBase<CopyMode::Copyable>& c)
+CallbackBase::CallbackBase(CallbackBase&& c) = default;
+CallbackBase& CallbackBase::operator=(CallbackBase&& c) = default;
+CallbackBase::CallbackBase(const CallbackBaseCopyable& c)
     : bind_state_(c.bind_state_) {}
 
-CallbackBase<CopyMode::MoveOnly>& CallbackBase<CopyMode::MoveOnly>::operator=(
-    const CallbackBase<CopyMode::Copyable>& c) {
+CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) {
   bind_state_ = c.bind_state_;
   return *this;
 }
 
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(
-    CallbackBase<CopyMode::Copyable>&& c)
+CallbackBase::CallbackBase(CallbackBaseCopyable&& c)
     : bind_state_(std::move(c.bind_state_)) {}
 
-CallbackBase<CopyMode::MoveOnly>& CallbackBase<CopyMode::MoveOnly>::operator=(
-    CallbackBase<CopyMode::Copyable>&& c) {
+CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) {
   bind_state_ = std::move(c.bind_state_);
   return *this;
 }
 
-void CallbackBase<CopyMode::MoveOnly>::Reset() {
+void CallbackBase::Reset() {
   // NULL the bind_state_ last, since it may be holding the last ref to whatever
   // object owns us, and we may be deleted after that.
   bind_state_ = nullptr;
 }
 
-bool CallbackBase<CopyMode::MoveOnly>::IsCancelled() const {
+bool CallbackBase::IsCancelled() const {
   DCHECK(bind_state_);
   return bind_state_->IsCancelled();
 }
 
-bool CallbackBase<CopyMode::MoveOnly>::EqualsInternal(
-    const CallbackBase& other) const {
+bool CallbackBase::EqualsInternal(const CallbackBase& other) const {
   return bind_state_ == other.bind_state_;
 }
 
-CallbackBase<CopyMode::MoveOnly>::CallbackBase(BindStateBase* bind_state)
+CallbackBase::CallbackBase(BindStateBase* bind_state)
     : bind_state_(bind_state ? AdoptRef(bind_state) : nullptr) {
   DCHECK(!bind_state_.get() || bind_state_->HasOneRef());
 }
 
-CallbackBase<CopyMode::MoveOnly>::~CallbackBase() {}
+CallbackBase::~CallbackBase() {}
 
-CallbackBase<CopyMode::Copyable>::CallbackBase(
-    const CallbackBase& c)
-    : CallbackBase<CopyMode::MoveOnly>(nullptr) {
+CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c)
+    : CallbackBase(nullptr) {
   bind_state_ = c.bind_state_;
 }
 
-CallbackBase<CopyMode::Copyable>::CallbackBase(CallbackBase&& c) = default;
+CallbackBaseCopyable::CallbackBaseCopyable(CallbackBaseCopyable&& c) = default;
 
-CallbackBase<CopyMode::Copyable>&
-CallbackBase<CopyMode::Copyable>::operator=(const CallbackBase& c) {
+CallbackBaseCopyable& CallbackBaseCopyable::operator=(
+    const CallbackBaseCopyable& c) {
   bind_state_ = c.bind_state_;
   return *this;
 }
 
-CallbackBase<CopyMode::Copyable>&
-CallbackBase<CopyMode::Copyable>::operator=(CallbackBase&& c) = default;
-
-template class CallbackBase<CopyMode::MoveOnly>;
-template class CallbackBase<CopyMode::Copyable>;
+CallbackBaseCopyable& CallbackBaseCopyable::operator=(
+    CallbackBaseCopyable&& c) = default;
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/callback_internal.h b/base/callback_internal.h
index 29b07c2..8ad8449 100644
--- a/base/callback_internal.h
+++ b/base/callback_internal.h
@@ -19,8 +19,8 @@
 
 namespace internal {
 
-template <CopyMode copy_mode>
 class CallbackBase;
+class CallbackBaseCopyable;
 
 class BindStateBase;
 
@@ -61,8 +61,8 @@
   friend struct BindStateBaseRefCountTraits;
   friend class RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits>;
 
-  template <CopyMode copy_mode>
   friend class CallbackBase;
+  friend class CallbackBaseCopyable;
 
   // Whitelist subclasses that access the destructor of BindStateBase.
   template <typename Functor, typename... BoundArgs>
@@ -90,17 +90,16 @@
 // template bloat.
 // CallbackBase<MoveOnly> is a direct base class of MoveOnly callbacks, and
 // CallbackBase<Copyable> uses CallbackBase<MoveOnly> for its implementation.
-template <>
-class BASE_EXPORT CallbackBase<CopyMode::MoveOnly> {
+class BASE_EXPORT CallbackBase {
  public:
   CallbackBase(CallbackBase&& c);
   CallbackBase& operator=(CallbackBase&& c);
 
-  explicit CallbackBase(const CallbackBase<CopyMode::Copyable>& c);
-  CallbackBase& operator=(const CallbackBase<CopyMode::Copyable>& c);
+  explicit CallbackBase(const CallbackBaseCopyable& c);
+  CallbackBase& operator=(const CallbackBaseCopyable& c);
 
-  explicit CallbackBase(CallbackBase<CopyMode::Copyable>&& c);
-  CallbackBase& operator=(CallbackBase<CopyMode::Copyable>&& c);
+  explicit CallbackBase(CallbackBaseCopyable&& c);
+  CallbackBase& operator=(CallbackBaseCopyable&& c);
 
   // Returns true if Callback is null (doesn't refer to anything).
   bool is_null() const { return !bind_state_; }
@@ -136,22 +135,18 @@
 };
 
 // CallbackBase<Copyable> is a direct base class of Copyable Callbacks.
-template <>
-class BASE_EXPORT CallbackBase<CopyMode::Copyable>
-    : public CallbackBase<CopyMode::MoveOnly> {
+class BASE_EXPORT CallbackBaseCopyable : public CallbackBase {
  public:
-  CallbackBase(const CallbackBase& c);
-  CallbackBase(CallbackBase&& c);
-  CallbackBase& operator=(const CallbackBase& c);
-  CallbackBase& operator=(CallbackBase&& c);
- protected:
-  explicit CallbackBase(BindStateBase* bind_state)
-      : CallbackBase<CopyMode::MoveOnly>(bind_state) {}
-  ~CallbackBase() {}
-};
+  CallbackBaseCopyable(const CallbackBaseCopyable& c);
+  CallbackBaseCopyable(CallbackBaseCopyable&& c);
+  CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c);
+  CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c);
 
-extern template class CallbackBase<CopyMode::MoveOnly>;
-extern template class CallbackBase<CopyMode::Copyable>;
+ protected:
+  explicit CallbackBaseCopyable(BindStateBase* bind_state)
+      : CallbackBase(bind_state) {}
+  ~CallbackBaseCopyable() {}
+};
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/callback_unittest.nc b/base/callback_unittest.nc
index 710362a2..3261529 100644
--- a/base/callback_unittest.nc
+++ b/base/callback_unittest.nc
@@ -15,7 +15,7 @@
 class Child : Parent {
 };
 
-#if defined(NCTEST_EQUALS_REQUIRES_SAMETYPE)  // [r"fatal error: no viable conversion from 'Callback<int \(\), \[2 \* \.\.\.\]>' to 'const Callback<void \(\), \[2 \* \.\.\.\]>'"]
+#if defined(NCTEST_EQUALS_REQUIRES_SAMETYPE)  // [r"fatal error: no viable conversion from 'RepeatingCallback<int \(\)>' to 'const RepeatingCallback<void \(\)>'"]
 
 // Attempting to call comparison function on two callbacks of different type.
 //
diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc
index 4be8899..5326658 100644
--- a/base/message_loop/message_pump_android.cc
+++ b/base/message_loop/message_pump_android.cc
@@ -117,11 +117,8 @@
 
 namespace base {
 
-MessagePumpForUI::MessagePumpForUI()
-    : run_loop_(nullptr), should_abort_(false) {}
-
-MessagePumpForUI::~MessagePumpForUI() {
-}
+MessagePumpForUI::MessagePumpForUI() = default;
+MessagePumpForUI::~MessagePumpForUI() = default;
 
 void MessagePumpForUI::Run(Delegate* delegate) {
   NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in"
@@ -129,6 +126,7 @@
 }
 
 JNIEnv* MessagePumpForUI::StartInternal() {
+  DCHECK(!quit_);
   run_loop_ = new RunLoop();
   // Since the RunLoop was just created above, BeforeRun should be guaranteed to
   // return true (it only returns false if the RunLoop has been Quit already).
@@ -159,6 +157,7 @@
 }
 
 void MessagePumpForUI::Quit() {
+  quit_ = true;
   if (!system_message_handler_obj_.is_null()) {
     JNIEnv* env = base::android::AttachCurrentThread();
     DCHECK(env);
@@ -176,6 +175,8 @@
 }
 
 void MessagePumpForUI::ScheduleWork() {
+  if (quit_)
+    return;
   DCHECK(!system_message_handler_obj_.is_null());
 
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -185,6 +186,8 @@
 }
 
 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+  if (quit_)
+    return;
   DCHECK(!system_message_handler_obj_.is_null());
 
   JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/base/message_loop/message_pump_android.h b/base/message_loop/message_pump_android.h
index 895d213..3c8ac654 100644
--- a/base/message_loop/message_pump_android.h
+++ b/base/message_loop/message_pump_android.h
@@ -50,9 +50,10 @@
  private:
   JNIEnv* StartInternal();
 
-  RunLoop* run_loop_;
+  RunLoop* run_loop_ = nullptr;
   base::android::ScopedJavaGlobalRef<jobject> system_message_handler_obj_;
-  bool should_abort_;
+  bool should_abort_ = false;
+  bool quit_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
 };
diff --git a/base/synchronization/waitable_event_mac.cc b/base/synchronization/waitable_event_mac.cc
index 3054a179..382c1d1b 100644
--- a/base/synchronization/waitable_event_mac.cc
+++ b/base/synchronization/waitable_event_mac.cc
@@ -4,13 +4,16 @@
 
 #include "base/synchronization/waitable_event.h"
 
+#include <dispatch/dispatch.h>
 #include <mach/mach.h>
 #include <sys/event.h>
 
 #include "base/debug/activity_tracker.h"
 #include "base/files/scoped_file.h"
+#include "base/mac/dispatch_source_mach.h"
 #include "base/mac/mac_util.h"
 #include "base/mac/mach_logging.h"
+#include "base/mac/scoped_dispatch_object.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
@@ -169,16 +172,24 @@
 
   // On macOS 10.11+, using Mach port sets may cause system instability, per
   // https://crbug.com/756102. On macOS 10.12+, a kqueue can be used
-  // instead to work around that. But on macOS 10.9-10.11, kqueue only works
-  // for port sets. On those platforms, port sets are just used directly. This
-  // does leave 10.11 susceptible to instability, though.
-  // TODO(rsesek): See if a libdispatch solution works on 10.11.
+  // instead to work around that. On macOS 10.9 and 10.10, kqueue only works
+  // for port sets, so port sets are just used directly. On macOS 10.11,
+  // libdispatch sources are used. Therefore, there are three different
+  // primitives that can be used to implement WaitMany. Which one to use is
+  // selected at run-time by OS version checks.
+  enum WaitManyPrimitive {
+    KQUEUE,
+    DISPATCH,
+    PORT_SET,
+  };
 #if defined(OS_IOS)
-  const bool use_kqueue = false;
+  const WaitManyPrimitive kPrimitive = PORT_SET;
 #else
-  const bool use_kqueue = mac::IsAtLeastOS10_12();
+  const WaitManyPrimitive kPrimitive =
+      mac::IsAtLeastOS10_12() ? KQUEUE
+                              : (mac::IsOS10_11() ? DISPATCH : PORT_SET);
 #endif
-  if (use_kqueue) {
+  if (kPrimitive == KQUEUE) {
     std::vector<kevent64_s> events(count);
     for (size_t i = 0; i < count; ++i) {
       EV_SET64(&events[i], raw_waitables[i]->receive_right_->Name(),
@@ -208,7 +219,54 @@
     }
 
     return triggered;
+  } else if (kPrimitive == DISPATCH) {
+    // Each item in |raw_waitables| will be watched using a dispatch souce
+    // scheduled on the serial |queue|. The first one to be invoked will
+    // signal the |semaphore| that this method will wait on.
+    ScopedDispatchObject<dispatch_queue_t> queue(dispatch_queue_create(
+        "org.chromium.base.WaitableEvent.WaitMany", DISPATCH_QUEUE_SERIAL));
+    ScopedDispatchObject<dispatch_semaphore_t> semaphore(
+        dispatch_semaphore_create(0));
+
+    // Block capture references. |signaled| will identify the index in
+    // |raw_waitables| whose source was invoked.
+    dispatch_semaphore_t semaphore_ref = semaphore.get();
+    const size_t kUnsignaled = -1;
+    __block size_t signaled = kUnsignaled;
+
+    // Create a MACH_RECV dispatch source for each event. These must be
+    // destroyed before the |queue| and |semaphore|.
+    std::vector<std::unique_ptr<DispatchSourceMach>> sources;
+    for (size_t i = 0; i < count; ++i) {
+      const bool auto_reset =
+          raw_waitables[i]->policy_ == WaitableEvent::ResetPolicy::AUTOMATIC;
+      // The block will copy a reference to |right|.
+      scoped_refptr<WaitableEvent::ReceiveRight> right =
+          raw_waitables[i]->receive_right_;
+      auto source =
+          std::make_unique<DispatchSourceMach>(queue, right->Name(), ^{
+            // After the semaphore is signaled, another event be signaled and
+            // the source may have its block put on the |queue|. WaitMany
+            // should only report (and auto-reset) one event, so the first
+            // event to signal is reported.
+            if (signaled == kUnsignaled) {
+              signaled = i;
+              if (auto_reset) {
+                PeekPort(right->Name(), true);
+              }
+              dispatch_semaphore_signal(semaphore_ref);
+            }
+          });
+      source->Resume();
+      sources.push_back(std::move(source));
+    }
+
+    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+    DCHECK_NE(signaled, kUnsignaled);
+    return signaled;
   } else {
+    DCHECK_EQ(kPrimitive, PORT_SET);
+
     kern_return_t kr;
 
     mac::ScopedMachPortSet port_set;
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index d861b5e..e543fd3b 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -836,12 +836,12 @@
   constants.SetOutputDirectory(output_directory)
   devil_chromium.Initialize(output_directory=output_directory)
   parser = argparse.ArgumentParser()
+  exists_or_None = lambda p: p if p and os.path.exists(p) else None
   parser.set_defaults(
       command_line_flags_file=command_line_flags_file,
       target_cpu=target_cpu,
-      apk_path=apk_path if os.path.exists(apk_path) else None,
-      incremental_json=(
-          incremental_json if os.path.exists(incremental_json) else None))
+      apk_path=exists_or_None(apk_path),
+      incremental_json=exists_or_None(incremental_json))
   _RunInternal(parser, output_directory=output_directory)
 
 
diff --git a/build/android/gyp/aar.py b/build/android/gyp/aar.py
index 6c0bc89..e8196e5a 100755
--- a/build/android/gyp/aar.py
+++ b/build/android/gyp/aar.py
@@ -38,76 +38,97 @@
   return True
 
 
+def _CreateInfo(aar_file):
+  data = {}
+  data['aidl'] = []
+  data['assets'] = []
+  data['resources'] = []
+  data['subjars'] = []
+  data['subjar_tuples'] = []
+  data['has_classes_jar'] = False
+  data['has_proguard_flags'] = False
+  data['has_native_libraries'] = False
+  data['has_r_text_file'] = False
+  with zipfile.ZipFile(aar_file) as z:
+    data['is_manifest_empty'] = (
+        _IsManifestEmpty(z.read('AndroidManifest.xml')))
+
+    for name in z.namelist():
+      if name.endswith('/'):
+        continue
+      if name.startswith('aidl/'):
+        data['aidl'].append(name)
+      elif name.startswith('res/'):
+        data['resources'].append(name)
+      elif name.startswith('libs/') and name.endswith('.jar'):
+        label = posixpath.basename(name)[:-4]
+        label = re.sub(r'[^a-zA-Z0-9._]', '_', label)
+        data['subjars'].append(name)
+        data['subjar_tuples'].append([label, name])
+      elif name.startswith('assets/'):
+        data['assets'].append(name)
+      elif name.startswith('jni/'):
+        data['has_native_libraries'] = True
+      elif name == 'classes.jar':
+        data['has_classes_jar'] = True
+      elif name == 'proguard.txt':
+        data['has_proguard_flags'] = True
+      elif name == 'R.txt':
+        # Some AARs, e.g. gvr_controller_java, have empty R.txt. Such AARs
+        # have no resources as well. We treat empty R.txt as having no R.txt.
+        data['has_r_text_file'] = (z.read('R.txt').strip() != '')
+
+  return """\
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+""" + gn_helpers.ToGNString(data)
+
+
+def _AddCommonArgs(parser):
+  parser.add_argument('aar_file',
+                      help='Path to the AAR file.',
+                      type=os.path.normpath)
+
+
 def main():
   parser = argparse.ArgumentParser(description=__doc__)
-  parser.add_argument('--input-file',
-                      help='Path to the AAR file.',
-                      required=True,
-                      metavar='FILE')
-  parser.add_argument('--extract',
-                      help='Extract the files to output directory.',
-                      action='store_true')
-  parser.add_argument('--list',
-                      help='List all the resource and jar files.',
-                      action='store_true')
-  parser.add_argument('--output-dir',
-                      help='Output directory for the extracted files. Must '
-                      'be set if --extract is set.',
-                      metavar='DIR')
+  command_parsers = parser.add_subparsers(dest='command')
+  subp = command_parsers.add_parser(
+      'list', help='Output a GN scope describing the contents of the .aar.')
+  _AddCommonArgs(subp)
+  subp.add_argument('--output',
+                    help='Output file.',
+                    type=argparse.FileType('w'),
+                    default='-')
+
+  subp = command_parsers.add_parser('extract', help='Extracts the .aar')
+  _AddCommonArgs(subp)
+  subp.add_argument('--output-dir',
+                    help='Output directory for the extracted files.',
+                    required=True,
+                    type=os.path.normpath)
+  subp.add_argument('--assert-info-file',
+                    help='Path to .info file. Asserts that it matches what '
+                         '"list" would output.',
+                    type=argparse.FileType('r'))
 
   args = parser.parse_args()
-  if not args.extract and not args.list:
-    parser.error('Either --extract or --list has to be specified.')
 
-  aar_file = args.input_file
-  output_dir = args.output_dir
-
-  if args.extract:
+  if args.command == 'extract':
+    if args.assert_info_file:
+      expected = _CreateInfo(args.aar_file)
+      actual = args.assert_info_file.read()
+      if actual != expected:
+        raise Exception('android_aar_prebuilt() cached .info file is '
+                        'out-of-date. Run gn gen with '
+                        'update_android_aar_prebuilts=true to update it.')
     # Clear previously extracted versions of the AAR.
-    shutil.rmtree(output_dir, True)
-    build_utils.ExtractAll(aar_file, path=output_dir)
+    shutil.rmtree(args.output_dir, True)
+    build_utils.ExtractAll(args.aar_file, path=args.output_dir)
 
-  if args.list:
-    data = {}
-    data['aidl'] = []
-    data['assets'] = []
-    data['resources'] = []
-    data['subjars'] = []
-    data['subjar_tuples'] = []
-    data['has_classes_jar'] = False
-    data['has_proguard_flags'] = False
-    data['has_native_libraries'] = False
-    data['has_r_text_file'] = False
-    with zipfile.ZipFile(aar_file) as z:
-      data['is_manifest_empty'] = (
-          _IsManifestEmpty(z.read('AndroidManifest.xml')))
-
-      for name in z.namelist():
-        if name.endswith('/'):
-          continue
-        if name.startswith('aidl/'):
-          data['aidl'].append(name)
-        elif name.startswith('res/'):
-          data['resources'].append(name)
-        elif name.startswith('libs/') and name.endswith('.jar'):
-          label = posixpath.basename(name)[:-4]
-          label = re.sub(r'[^a-zA-Z0-9._]', '_', label)
-          data['subjars'].append(name)
-          data['subjar_tuples'].append([label, name])
-        elif name.startswith('assets/'):
-          data['assets'].append(name)
-        elif name.startswith('jni/'):
-          data['has_native_libraries'] = True
-        elif name == 'classes.jar':
-          data['has_classes_jar'] = True
-        elif name == 'proguard.txt':
-          data['has_proguard_flags'] = True
-        elif name == 'R.txt':
-          # Some AARs, e.g. gvr_controller_java, have empty R.txt. Such AARs
-          # have no resources as well. We treat empty R.txt as having no R.txt.
-          data['has_r_text_file'] = (z.read('R.txt').strip() != '')
-
-    print gn_helpers.ToGNString(data)
+  elif args.command == 'list':
+    args.output.write(_CreateInfo(args.aar_file))
 
 
 if __name__ == '__main__':
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 42ae442..367aa2f 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -159,8 +159,10 @@
     if 'stack' in bundle:
       if symbolizer and device_abi:
         current_result.SetLog(
-            '\n'.join(symbolizer.ExtractAndResolveNativeStackTraces(
-                bundle['stack'], device_abi)))
+            '%s\n%s' % (
+              bundle['stack'],
+              '\n'.join(symbolizer.ExtractAndResolveNativeStackTraces(
+                  bundle['stack'], device_abi))))
       else:
         current_result.SetLog(bundle['stack'])
 
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 6723744..35fab78 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -164,6 +164,10 @@
     # Ex. with this arg set to true, the chrome_public_apk target result in
     # chrome_public_apk_incremental being built.
     incremental_apk_by_default = false
+
+    # When true, updates all android_aar_prebuilt() .info files during gn gen.
+    # Refer to android_aar_prebuilt() for more details.
+    update_android_aar_prebuilts = false
   }
 
   # We need a second declare_args block to make sure we are using the overridden
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 429bc32..27ba696e 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2934,22 +2934,33 @@
   # resources. For libraries without resources, it will not generate
   # corresponding android_resources targets.
   #
+  # To avoid slowing down "gn gen", an associated .info file must be committed
+  # along with the .aar file. In order to create this file, define the target
+  # and then run once with the gn arg "update_android_aar_prebuilts = true".
+  #
   # Variables
   #   aar_path: Path to the AAR.
+  #   info_path: Path to the .aar.info file (generated via
+  #       update_android_aar_prebuilts GN arg).
   #   proguard_configs: List of proguard configs to use in final apk step for
-  #     any apk that depends on this library.
+  #       any apk that depends on this library.
   #   ignore_aidl: Whether to ignore .aidl files found with the .aar.
   #   ignore_assets: Whether to ignore assets found in the .aar.
   #   ignore_native_libraries: Whether to ignore .so files found in the .aar.
   #   create_srcjar: If false, does not create an R.java file.
   #   TODO(jbudorick@): remove this arguments after crbug.com/522043 is fixed.
-  #   requires_android: Whether this target can only be used for compiling Android related targets.
+  #   requires_android: Whether this target can only be used for compiling
+  #       Android related targets.
   #
   # Example
   #   android_aar_prebuilt("foo_java") {
   #     aar_path = "foo.aar"
   #   }
   template("android_aar_prebuilt") {
+    _info_path = "$target_name.info"
+    if (defined(invoker.info_path)) {
+      _info_path = invoker.info_path
+    }
     _output_path = "${target_gen_dir}/${target_name}"
     _unpack_target_name = "${target_name}__unpack_aar"
     _ignore_aidl = defined(invoker.ignore_aidl) && invoker.ignore_aidl
@@ -2959,14 +2970,17 @@
 
     # Scan the AAR file and determine the resources and jar files.
     # Some libraries might not have resources; others might have two jars.
-    _scanned_files =
-        exec_script("//build/android/gyp/aar.py",
-                    [
-                      "--input-file",
-                      rebase_path(invoker.aar_path, root_build_dir),
-                      "--list",
-                    ],
-                    "scope")
+    if (update_android_aar_prebuilts) {
+      print("Writing " + rebase_path(_info_path, "//"))
+      exec_script("//build/android/gyp/aar.py",
+                  [
+                    "list",
+                    rebase_path(invoker.aar_path, root_build_dir),
+                    "--output",
+                    rebase_path(_info_path, root_build_dir),
+                  ])
+    }
+    _scanned_files = read_file(_info_path, "scope")
 
     assert(_ignore_aidl || _scanned_files.aidl == [],
            "android_aar_prebuilt() aidl not yet supported." +
@@ -2984,11 +2998,12 @@
     action(_unpack_target_name) {
       script = "//build/android/gyp/aar.py"  # Unzips the AAR
       args = [
-        "--input-file",
+        "extract",
         rebase_path(invoker.aar_path, root_build_dir),
         "--output-dir",
         rebase_path(_output_path, root_build_dir),
-        "--extract",
+        "--assert-info-file",
+        rebase_path(_info_path, root_build_dir),
       ]
       inputs = [
         invoker.aar_path,
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn
index e9e7058..58a851c 100644
--- a/build/secondary/third_party/android_tools/BUILD.gn
+++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -59,6 +59,7 @@
   ]
   _lib_name = "design"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_transition_java") {
@@ -67,10 +68,12 @@
   ]
   _lib_name = "transition"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_multidex_java") {
   aar_path = "$lib_path/multidex/1.0.1/multidex-1.0.1.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_java_prebuilt("android_support_annotations_java") {
@@ -94,6 +97,7 @@
   ]
   _lib_name = "support-compat"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
   ignore_aidl = true  # We don't appear to need these currently.
 }
 
@@ -103,6 +107,7 @@
   ]
   _lib_name = "support-core-ui"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_core_utils_java") {
@@ -111,6 +116,7 @@
   ]
   _lib_name = "support-core-utils"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_fragment_java") {
@@ -122,6 +128,7 @@
   ]
   _lib_name = "support-fragment"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_media_compat_java") {
@@ -130,6 +137,7 @@
   ]
   _lib_name = "support-media-compat"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
   ignore_aidl = true  # We don't appear to need these currently.
 }
 
@@ -139,6 +147,7 @@
   ]
   _lib_name = "support-v13"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_vector_drawable_java") {
@@ -147,6 +156,7 @@
   ]
   _lib_name = "support-vector-drawable"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v7_appcompat_java_internal") {
@@ -155,6 +165,7 @@
   ]
   _lib_name = "appcompat-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 java_group("android_support_v7_appcompat_java") {
@@ -172,6 +183,7 @@
   ]
   _lib_name = "gridlayout-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v7_mediarouter_java") {
@@ -181,6 +193,7 @@
   ]
   _lib_name = "mediarouter-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v7_recyclerview_java") {
@@ -191,6 +204,7 @@
   ]
   _lib_name = "recyclerview-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v7_palette_java") {
@@ -200,6 +214,7 @@
   ]
   _lib_name = "palette-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v7_preference_java") {
@@ -210,6 +225,7 @@
   ]
   _lib_name = "preference-v7"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v14_preference_java") {
@@ -221,6 +237,7 @@
   ]
   _lib_name = "preference-v14"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v17_leanback_java") {
@@ -230,6 +247,7 @@
   ]
   _lib_name = "leanback-v17"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("android_support_v17_preference_java") {
@@ -243,6 +261,7 @@
   ]
   _lib_name = "preference-leanback-v17"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_library("android_support_chromium_java") {
@@ -282,6 +301,7 @@
   ]
   _lib_name = "play-services-basement"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
   input_jars_paths = [ "$android_sdk/optional/org.apache.http.legacy.jar" ]
 }
 
@@ -291,6 +311,7 @@
   ]
   _lib_name = "play-services-tasks"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_base_java") {
@@ -301,6 +322,7 @@
   ]
   _lib_name = "play-services-base"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_auth_base_java") {
@@ -310,6 +332,7 @@
   ]
   _lib_name = "play-services-auth-base"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_auth_java") {
@@ -320,6 +343,7 @@
   ]
   _lib_name = "play-services-auth"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_cast_java") {
@@ -330,6 +354,7 @@
   ]
   _lib_name = "play-services-cast"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_iid_java") {
@@ -339,6 +364,7 @@
   ]
   _lib_name = "play-services-iid"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_gcm_java") {
@@ -349,6 +375,7 @@
   ]
   _lib_name = "play-services-gcm"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_location_java") {
@@ -358,6 +385,7 @@
   ]
   _lib_name = "play-services-location"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_nearby_java") {
@@ -367,6 +395,7 @@
   ]
   _lib_name = "play-services-nearby"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 android_aar_prebuilt("google_play_services_vision_java") {
@@ -376,6 +405,7 @@
   ]
   _lib_name = "play-services-vision"
   aar_path = "$gms_path/$_lib_name/$gms_version/$_lib_name-$gms_version.aar"
+  info_path = "//build/secondary/third_party/android_tools/$target_name.info"
 }
 
 # TODO(paulmiller): Replace this with a proper target after rolling to a GMS
diff --git a/build/secondary/third_party/android_tools/android_support_compat_java.info b/build/secondary/third_party/android_tools/android_support_compat_java.info
new file mode 100644
index 0000000..b68ec40
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_compat_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [ "aidl/android/support/v4/os/ResultReceiver.aidl" ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_core_ui_java.info b/build/secondary/third_party/android_tools/android_support_core_ui_java.info
new file mode 100644
index 0000000..5f0071dd
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_core_ui_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_core_utils_java.info b/build/secondary/third_party/android_tools/android_support_core_utils_java.info
new file mode 100644
index 0000000..d82eb75
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_core_utils_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_design_java.info b/build/secondary/third_party/android_tools/android_support_design_java.info
new file mode 100644
index 0000000..37e2270
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_design_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/drawable-v21/design_bottom_navigation_item_background.xml", "res/values-v21/values-v21.xml", "res/anim-v21/design_bottom_sheet_slide_out.xml", "res/anim-v21/design_appbar_state_list_animator.xml", "res/anim-v21/design_bottom_sheet_slide_in.xml", "res/anim/design_fab_out.xml", "res/anim/design_bottom_sheet_slide_out.xml", "res/anim/design_snackbar_in.xml", "res/anim/design_fab_in.xml", "res/anim/design_snackbar_out.xml", "res/anim/design_bottom_sheet_slide_in.xml", "res/layout/design_layout_tab_text.xml", "res/layout/design_navigation_item_separator.xml", "res/layout/design_bottom_sheet_dialog.xml", "res/layout/design_layout_snackbar_include.xml", "res/layout/design_text_input_password_icon.xml", "res/layout/design_navigation_menu_item.xml", "res/layout/design_menu_item_action_area.xml", "res/layout/design_navigation_item.xml", "res/layout/design_navigation_item_header.xml", "res/layout/design_layout_snackbar.xml", "res/layout/design_navigation_item_subheader.xml", "res/layout/design_navigation_menu.xml", "res/layout/design_layout_tab_icon.xml", "res/layout/design_bottom_navigation_item.xml", "res/values-sw600dp-v13/values-sw600dp-v13.xml", "res/values-land/values-land.xml", "res/color-v23/design_tint_password_toggle.xml", "res/values/values.xml", "res/layout-sw600dp-v13/design_layout_snackbar.xml", "res/color/design_error.xml", "res/color/design_tint_password_toggle.xml", "res/drawable/design_snackbar_background.xml", "res/drawable/design_fab_background.xml", "res/drawable/design_bottom_navigation_item_background.xml", "res/drawable/design_ic_visibility.xml", "res/drawable/navigation_empty_icon.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_fragment_java.info b/build/secondary/third_party/android_tools/android_support_fragment_java.info
new file mode 100644
index 0000000..d82eb75
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_fragment_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_media_compat_java.info b/build/secondary/third_party/android_tools/android_support_media_compat_java.info
new file mode 100644
index 0000000..347df34
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_media_compat_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [ "aidl/android/support/v4/media/session/MediaSessionCompat.aidl", "aidl/android/support/v4/media/session/PlaybackStateCompat.aidl", "aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl", "aidl/android/support/v4/media/RatingCompat.aidl", "aidl/android/support/v4/media/MediaMetadataCompat.aidl" ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_multidex_java.info b/build/secondary/third_party/android_tools/android_support_multidex_java.info
new file mode 100644
index 0000000..a2ebd4a
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_multidex_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_transition_java.info b/build/secondary/third_party/android_tools/android_support_transition_java.info
new file mode 100644
index 0000000..28c8330
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_transition_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v13_java.info b/build/secondary/third_party/android_tools/android_support_v13_java.info
new file mode 100644
index 0000000..d82eb75
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v13_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_v14_preference_java.info b/build/secondary/third_party/android_tools/android_support_v14_preference_java.info
new file mode 100644
index 0000000..e7f9be67
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v14_preference_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/layout-v17/preference_material.xml", "res/layout-v17/preference_information_material.xml", "res/layout-v17/preference_category_material.xml", "res/layout-v17/preference_dropdown_material.xml", "res/values-v17/values-v17.xml", "res/layout/preference_material.xml", "res/layout/preference_information_material.xml", "res/layout/preference_category_material.xml", "res/layout/preference_dropdown_material.xml", "res/layout/preference_widget_switch.xml", "res/values/values.xml", "res/layout-v21/preference_material.xml", "res/layout-v21/preference_information_material.xml", "res/layout-v21/preference_category_material.xml", "res/layout-v21/preference_dropdown_material.xml", "res/drawable/preference_list_divider_material.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v17_leanback_java.info b/build/secondary/third_party/android_tools/android_support_v17_leanback_java.info
new file mode 100644
index 0000000..93dae49
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v17_leanback_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values-km-rKH/values-km-rKH.xml", "res/values-nl/values-nl.xml", "res/values-az-rAZ/values-az-rAZ.xml", "res/values-gl-rES/values-gl-rES.xml", "res/values-bg/values-bg.xml", "res/values-ldrtl-v17/values-ldrtl-v17.xml", "res/values-pt-rBR/values-pt-rBR.xml", "res/values-eu-rES/values-eu-rES.xml", "res/values-el/values-el.xml", "res/animator/lb_onboarding_page_indicator_enter.xml", "res/animator/lb_guidedactions_item_unpressed.xml", "res/animator/lb_onboarding_logo_enter.xml", "res/animator/lb_guidedactions_item_pressed.xml", "res/animator/lb_onboarding_start_button_fade_out.xml", "res/animator/lb_playback_rows_fade_out.xml", "res/animator/lb_onboarding_start_button_fade_in.xml", "res/animator/lb_decelerator_4.xml", "res/animator/lb_onboarding_logo_exit.xml", "res/animator/lb_guidedstep_slide_up.xml", "res/animator/lb_onboarding_title_enter.xml", "res/animator/lb_onboarding_page_indicator_fade_out.xml", "res/animator/lb_playback_controls_fade_in.xml", "res/animator/lb_playback_rows_fade_in.xml", "res/animator/lb_playback_description_fade_in.xml", "res/animator/lb_playback_controls_fade_out.xml", "res/animator/lb_onboarding_page_indicator_fade_in.xml", "res/animator/lb_playback_description_fade_out.xml", "res/animator/lb_playback_bg_fade_out.xml", "res/animator/lb_decelerator_2.xml", "res/animator/lb_guidedstep_slide_down.xml", "res/animator/lb_playback_bg_fade_in.xml", "res/animator/lb_onboarding_description_enter.xml", "res/values-mk-rMK/values-mk-rMK.xml", "res/values-af/values-af.xml", "res/raw/lb_voice_open.ogg", "res/raw/lb_voice_no_input.ogg", "res/raw/lb_voice_failure.ogg", "res/raw/lb_voice_success.ogg", "res/drawable-v21/lb_card_foreground.xml", "res/drawable-v21/lb_selectable_item_rounded_rect.xml", "res/drawable-v21/lb_action_bg.xml", "res/drawable-v21/lb_control_button_secondary.xml", "res/drawable-v21/lb_control_button_primary.xml", "res/values-uk/values-uk.xml", "res/transition-v21/lb_vertical_grid_entrance_transition.xml", "res/transition-v21/lb_vertical_grid_enter_transition.xml", "res/transition-v21/lb_browse_headers_in.xml", "res/transition-v21/lb_browse_return_transition.xml", "res/transition-v21/lb_title_in.xml", "res/transition-v21/lb_return_transition.xml", "res/transition-v21/lb_browse_enter_transition.xml", "res/transition-v21/lb_guidedstep_activity_enter.xml", "res/transition-v21/lb_browse_headers_out.xml", "res/transition-v21/lb_shared_element_enter_transition.xml", "res/transition-v21/lb_details_return_transition.xml", "res/transition-v21/lb_shared_element_return_transition.xml", "res/transition-v21/lb_title_out.xml", "res/transition-v21/lb_guidedstep_activity_enter_bottom.xml", "res/transition-v21/lb_enter_transition.xml", "res/transition-v21/lb_vertical_grid_return_transition.xml", "res/transition-v21/lb_browse_entrance_transition.xml", "res/transition-v21/lb_details_enter_transition.xml", "res/values-zu/values-zu.xml", "res/values-et-rEE/values-et-rEE.xml", "res/values-v21/values-v21.xml", "res/values-lo-rLA/values-lo-rLA.xml", "res/drawable-hdpi-v4/lb_ic_search_mic_out.png", "res/drawable-hdpi-v4/lb_ic_sad_cloud.png", "res/drawable-hdpi-v4/lb_in_app_search_shadow_normal.9.png", "res/drawable-hdpi-v4/lb_in_app_search_shadow_focused.9.png", "res/drawable-hdpi-v4/lb_ic_actions_right_arrow.png", "res/drawable-hdpi-v4/lb_action_bg_focused.9.png", "res/drawable-hdpi-v4/lb_in_app_search_bg.9.png", "res/drawable-hdpi-v4/lb_ic_search_mic.png", "res/drawable-hdpi-v4/lb_ic_in_app_search.png", "res/values-v22/values-v22.xml", "res/values-bn-rBD/values-bn-rBD.xml", "res/values-is-rIS/values-is-rIS.xml", "res/values-gu-rIN/values-gu-rIN.xml", "res/drawable-mdpi-v4/lb_ic_search_mic_out.png", "res/drawable-mdpi-v4/lb_ic_sad_cloud.png", "res/drawable-mdpi-v4/lb_in_app_search_shadow_normal.9.png", "res/drawable-mdpi-v4/lb_in_app_search_shadow_focused.9.png", "res/drawable-mdpi-v4/lb_ic_actions_right_arrow.png", "res/drawable-mdpi-v4/lb_action_bg_focused.9.png", "res/drawable-mdpi-v4/lb_in_app_search_bg.9.png", "res/drawable-mdpi-v4/lb_ic_search_mic.png", "res/drawable-mdpi-v4/lb_ic_in_app_search.png", "res/values-pa-rIN/values-pa-rIN.xml", "res/values-mr-rIN/values-mr-rIN.xml", "res/values-ru/values-ru.xml", "res/values-pl/values-pl.xml", "res/values-fa/values-fa.xml", "res/values-be-rBY/values-be-rBY.xml", "res/values-si-rLK/values-si-rLK.xml", "res/values-sv/values-sv.xml", "res/values-my-rMM/values-my-rMM.xml", "res/values-b+sr+Latn/values-b+sr+Latn.xml", "res/values-fr/values-fr.xml", "res/values-uz-rUZ/values-uz-rUZ.xml", "res/values-ms-rMY/values-ms-rMY.xml", "res/values-ka-rGE/values-ka-rGE.xml", "res/values-fi/values-fi.xml", "res/values-fr-rCA/values-fr-rCA.xml", "res/values-ca/values-ca.xml", "res/values-in/values-in.xml", "res/values-vi/values-vi.xml", "res/layout/lb_row_media_item_action.xml", "res/layout/lb_error_fragment.xml", "res/layout/lb_picker.xml", "res/layout/lb_speech_orb.xml", "res/layout/lb_details_fragment.xml", "res/layout/lb_divider.xml", "res/layout/lb_guidedstep_fragment.xml", "res/layout/lb_background_window.xml", "res/layout/lb_rows_fragment.xml", "res/layout/lb_guidedstep_background.xml", "res/layout/lb_details_description.xml", "res/layout/lb_media_item_number_view_flipper.xml", "res/layout/lb_action_1_line.xml", "res/layout/lb_guidedbuttonactions.xml", "res/layout/lb_control_bar.xml", "res/layout/lb_vertical_grid_fragment.xml", "res/layout/lb_headers_fragment.xml", "res/layout/lb_guidance.xml", "res/layout/lb_fullwidth_details_overview_logo.xml", "res/layout/lb_header.xml", "res/layout/lb_browse_fragment.xml", "res/layout/lb_row_header.xml", "res/layout/lb_vertical_grid.xml", "res/layout/lb_control_button_secondary.xml", "res/layout/lb_details_overview.xml", "res/layout/lb_image_card_view_themed_badge_left.xml", "res/layout/lb_search_bar.xml", "res/layout/lb_list_row_hovercard.xml", "res/layout/lb_picker_column.xml", "res/layout/lb_image_card_view_themed_content.xml", "res/layout/lb_playback_now_playing_bars.xml", "res/layout/lb_row_media_item.xml", "res/layout/lb_shadow.xml", "res/layout/lb_picker_item.xml", "res/layout/lb_playback_controls_row.xml", "res/layout/lb_fullwidth_details_overview.xml", "res/layout/lb_guidedactions_item.xml", "res/layout/lb_control_button_primary.xml", "res/layout/lb_section_header.xml", "res/layout/lb_playback_controls.xml", "res/layout/lb_image_card_view_themed_badge_right.xml", "res/layout/lb_picker_separator.xml", "res/layout/lb_title_view.xml", "res/layout/lb_image_card_view.xml", "res/layout/lb_guidedactions_datepicker_item.xml", "res/layout/lb_action_2_lines.xml", "res/layout/lb_guidedactions.xml", "res/layout/lb_search_orb.xml", "res/layout/lb_search_fragment.xml", "res/layout/lb_row_container.xml", "res/layout/lb_list_row.xml", "res/layout/lb_browse_title.xml", "res/layout/lb_media_list_header.xml", "res/layout/lb_onboarding_fragment.xml", "res/layout/lb_image_card_view_themed_title.xml", "res/values-en-rGB/values-en-rGB.xml", "res/values-ja/values-ja.xml", "res/values-zh-rCN/values-zh-rCN.xml", "res/values-am/values-am.xml", "res/values-sq-rAL/values-sq-rAL.xml", "res/values-sl/values-sl.xml", "res/values-de/values-de.xml", "res/values-kk-rKZ/values-kk-rKZ.xml", "res/values-en-rAU/values-en-rAU.xml", "res/values-en-rIN/values-en-rIN.xml", "res/values-zh-rTW/values-zh-rTW.xml", "res/values-sw/values-sw.xml", "res/values-nb/values-nb.xml", "res/values-bs-rBA/values-bs-rBA.xml", "res/values-tr/values-tr.xml", "res/values-kn-rIN/values-kn-rIN.xml", "res/transition-v19/lb_browse_headers_in.xml", "res/transition-v19/lb_browse_headers_out.xml", "res/values-hy-rAM/values-hy-rAM.xml", "res/values-es-rUS/values-es-rUS.xml", "res/values-zh-rHK/values-zh-rHK.xml", "res/values-ko/values-ko.xml", "res/values-pt-rPT/values-pt-rPT.xml", "res/values-iw/values-iw.xml", "res/values-lt/values-lt.xml", "res/values-ro/values-ro.xml", "res/animator-v21/lb_playback_description_fade_out.xml", "res/animator-v21/lb_playback_bg_fade_out.xml", "res/animator-v21/lb_playback_bg_fade_in.xml", "res/values-pt/values-pt.xml", "res/values-ar/values-ar.xml", "res/values-ur-rPK/values-ur-rPK.xml", "res/values-ml-rIN/values-ml-rIN.xml", "res/drawable-xhdpi-v4/lb_ic_replay.png", "res/drawable-xhdpi-v4/lb_ic_nav_arrow.png", "res/drawable-xhdpi-v4/lb_text_dot_two_small.png", "res/drawable-xhdpi-v4/lb_text_dot_one_small.png", "res/drawable-xhdpi-v4/lb_ic_skip_previous.png", "res/drawable-xhdpi-v4/lb_ic_thumb_down.png", "res/drawable-xhdpi-v4/lb_ic_thumb_up_outline.png", "res/drawable-xhdpi-v4/lb_ic_search_mic_out.png", "res/drawable-xhdpi-v4/lb_ic_loop.png", "res/drawable-xhdpi-v4/lb_ic_play_fit.png", "res/drawable-xhdpi-v4/lb_ic_thumb_up.png", "res/drawable-xhdpi-v4/lb_ic_guidedactions_item_chevron.png", "res/drawable-xhdpi-v4/lb_ic_fast_rewind.png", "res/drawable-xhdpi-v4/lb_ic_shuffle.png", "res/drawable-xhdpi-v4/lb_ic_sad_cloud.png", "res/drawable-xhdpi-v4/lb_in_app_search_shadow_normal.9.png", "res/drawable-xhdpi-v4/lb_ic_fast_forward.png", "res/drawable-xhdpi-v4/lb_card_shadow_focused.9.png", "res/drawable-xhdpi-v4/lb_in_app_search_shadow_focused.9.png", "res/drawable-xhdpi-v4/lb_ic_actions_right_arrow.png", "res/drawable-xhdpi-v4/lb_action_bg_focused.9.png", "res/drawable-xhdpi-v4/lb_ic_hq.png", "res/drawable-xhdpi-v4/lb_in_app_search_bg.9.png", "res/drawable-xhdpi-v4/lb_ic_stop.png", "res/drawable-xhdpi-v4/lb_ic_search_mic.png", "res/drawable-xhdpi-v4/lb_card_shadow_normal.9.png", "res/drawable-xhdpi-v4/lb_ic_loop_one.png", "res/drawable-xhdpi-v4/lb_ic_in_app_search.png", "res/drawable-xhdpi-v4/lb_text_dot_one.png", "res/drawable-xhdpi-v4/lb_ic_pip.png", "res/drawable-xhdpi-v4/lb_text_dot_two.png", "res/drawable-xhdpi-v4/lb_ic_more.png", "res/drawable-xhdpi-v4/lb_ic_pause.png", "res/drawable-xhdpi-v4/lb_ic_cc.png", "res/drawable-xhdpi-v4/lb_ic_skip_next.png", "res/drawable-xhdpi-v4/lb_ic_thumb_down_outline.png", "res/drawable-xhdpi-v4/lb_ic_playback_loop.png", "res/drawable-xhdpi-v4/lb_ic_play.png", "res/values/values.xml", "res/values-hr/values-hr.xml", "res/values-mn-rMN/values-mn-rMN.xml", "res/values-da/values-da.xml", "res/values-hi/values-hi.xml", "res/values-it/values-it.xml", "res/values-es/values-es.xml", "res/values-sk/values-sk.xml", "res/values-lv/values-lv.xml", "res/values-ky-rKG/values-ky-rKG.xml", "res/values-hu/values-hu.xml", "res/drawable-xxhdpi-v4/lb_ic_search_mic_out.png", "res/drawable-xxhdpi-v4/lb_ic_sad_cloud.png", "res/drawable-xxhdpi-v4/lb_in_app_search_shadow_normal.9.png", "res/drawable-xxhdpi-v4/lb_in_app_search_shadow_focused.9.png", "res/drawable-xxhdpi-v4/lb_ic_actions_right_arrow.png", "res/drawable-xxhdpi-v4/lb_action_bg_focused.9.png", "res/drawable-xxhdpi-v4/lb_in_app_search_bg.9.png", "res/drawable-xxhdpi-v4/lb_ic_search_mic.png", "res/drawable-xxhdpi-v4/lb_ic_in_app_search.png", "res/values-v19/values-v19.xml", "res/values-th/values-th.xml", "res/values-cs/values-cs.xml", "res/values-sr/values-sr.xml", "res/values-tl/values-tl.xml", "res/values-te-rIN/values-te-rIN.xml", "res/values-ne-rNP/values-ne-rNP.xml", "res/values-ta-rIN/values-ta-rIN.xml", "res/drawable/lb_speech_orb.xml", "res/drawable/lb_playback_progress_bar.xml", "res/drawable/lb_card_foreground.xml", "res/drawable/lb_onboarding_start_button_background.xml", "res/drawable/lb_control_button_secondary.xml", "res/drawable/lb_playback_now_playing_bar.xml", "res/drawable/lb_control_button_primary.xml", "res/drawable/lb_background.xml", "res/drawable/lb_headers_right_fading.xml", "res/drawable/lb_search_orb.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v17_preference_java.info b/build/secondary/third_party/android_tools/android_support_v17_preference_java.info
new file mode 100644
index 0000000..e97682c1
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v17_preference_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/layout/leanback_preference_category.xml", "res/layout/leanback_list_preference_item_single.xml", "res/layout/leanback_list_preference_fragment.xml", "res/layout/leanback_list_preference_item_multi.xml", "res/layout/leanback_settings_fragment.xml", "res/layout/leanback_preference_fragment.xml", "res/layout/leanback_preferences_list.xml", "res/layout/leanback_preference_information.xml", "res/layout/leanback_preference.xml", "res/values/values.xml", "res/layout-v21/leanback_settings_fragment.xml", "res/color/lb_preference_item_secondary_text_color.xml", "res/color/lb_preference_item_primary_text_color.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_appcompat_java_internal.info b/build/secondary/third_party/android_tools/android_support_v7_appcompat_java_internal.info
new file mode 100644
index 0000000..780717bb
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_appcompat_java_internal.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values-v23/values-v23.xml", "res/values-km-rKH/values-km-rKH.xml", "res/values-nl/values-nl.xml", "res/values-az-rAZ/values-az-rAZ.xml", "res/values-port/values-port.xml", "res/values-gl-rES/values-gl-rES.xml", "res/values-bg/values-bg.xml", "res/values-v18/values-v18.xml", "res/values-xlarge-v4/values-xlarge-v4.xml", "res/values-pt-rBR/values-pt-rBR.xml", "res/values-eu-rES/values-eu-rES.xml", "res/values-el/values-el.xml", "res/values-mk-rMK/values-mk-rMK.xml", "res/values-af/values-af.xml", "res/drawable-ldrtl-xhdpi-v17/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-ldrtl-xhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-ldrtl-xhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png", "res/drawable-ldrtl-xxxhdpi-v17/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-ldrtl-xxxhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-ldrtl-xxxhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png", "res/drawable-v21/abc_btn_colored_material.xml", "res/drawable-v21/abc_edit_text_material.xml", "res/drawable-v21/notification_action_background.xml", "res/drawable-v21/abc_action_bar_item_background_material.xml", "res/values-uk/values-uk.xml", "res/values-zu/values-zu.xml", "res/values-et-rEE/values-et-rEE.xml", "res/values-v21/values-v21.xml", "res/values-lo-rLA/values-lo-rLA.xml", "res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png", "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "res/drawable-hdpi-v4/abc_list_focused_holo.9.png", "res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png", "res/drawable-hdpi-v4/abc_ic_star_black_36dp.png", "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_light.png", "res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png", "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png", "res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png", "res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png", "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_dark.png", "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_light.png", "res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "res/drawable-hdpi-v4/notification_bg_low_normal.9.png", "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_dark.png", "res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_ic_star_black_48dp.png", "res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_dark.png", "res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "res/drawable-hdpi-v4/abc_ic_star_half_black_16dp.png", "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "res/drawable-hdpi-v4/notification_bg_normal.9.png", "res/drawable-hdpi-v4/abc_ic_star_half_black_36dp.png", "res/drawable-hdpi-v4/notify_panel_notification_icon_bg.png", "res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_light.png", "res/drawable-hdpi-v4/notification_bg_low_pressed.9.png", "res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "res/drawable-hdpi-v4/notification_bg_normal_pressed.9.png", "res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "res/drawable-hdpi-v4/abc_ic_star_black_16dp.png", "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png", "res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "res/drawable-hdpi-v4/abc_ic_star_half_black_48dp.png", "res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "res/values-v22/values-v22.xml", "res/values-bn-rBD/values-bn-rBD.xml", "res/values-v24/values-v24.xml", "res/values-is-rIS/values-is-rIS.xml", "res/values-gu-rIN/values-gu-rIN.xml", "res/drawable-mdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "res/drawable-mdpi-v4/abc_list_pressed_holo_light.9.png", "res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "res/drawable-mdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "res/drawable-mdpi-v4/abc_list_focused_holo.9.png", "res/drawable-mdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "res/drawable-mdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_popup_background_mtrl_mult.9.png", "res/drawable-mdpi-v4/abc_ic_star_black_36dp.png", "res/drawable-mdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "res/drawable-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-mdpi-v4/abc_text_select_handle_left_mtrl_light.png", "res/drawable-mdpi-v4/abc_list_longpressed_holo.9.png", "res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_000.png", "res/drawable-mdpi-v4/abc_list_pressed_holo_dark.9.png", "res/drawable-mdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_015.png", "res/drawable-mdpi-v4/abc_text_select_handle_left_mtrl_dark.png", "res/drawable-mdpi-v4/abc_text_select_handle_middle_mtrl_light.png", "res/drawable-mdpi-v4/abc_switch_track_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "res/drawable-mdpi-v4/notification_bg_low_normal.9.png", "res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "res/drawable-mdpi-v4/abc_text_select_handle_middle_mtrl_dark.png", "res/drawable-mdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_ic_star_black_48dp.png", "res/drawable-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-mdpi-v4/abc_list_divider_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_text_select_handle_right_mtrl_dark.png", "res/drawable-mdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "res/drawable-mdpi-v4/abc_ic_star_half_black_16dp.png", "res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "res/drawable-mdpi-v4/notification_bg_normal.9.png", "res/drawable-mdpi-v4/abc_ic_star_half_black_36dp.png", "res/drawable-mdpi-v4/notify_panel_notification_icon_bg.png", "res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_text_select_handle_right_mtrl_light.png", "res/drawable-mdpi-v4/notification_bg_low_pressed.9.png", "res/drawable-mdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "res/drawable-mdpi-v4/notification_bg_normal_pressed.9.png", "res/drawable-mdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "res/drawable-mdpi-v4/abc_ic_star_black_16dp.png", "res/drawable-mdpi-v4/abc_list_selector_disabled_holo_light.9.png", "res/drawable-mdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "res/drawable-mdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "res/drawable-mdpi-v4/abc_ic_star_half_black_48dp.png", "res/drawable-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "res/values-pa-rIN/values-pa-rIN.xml", "res/values-mr-rIN/values-mr-rIN.xml", "res/values-ru/values-ru.xml", "res/values-pl/values-pl.xml", "res/values-fa/values-fa.xml", "res/values-v11/values-v11.xml", "res/values-be-rBY/values-be-rBY.xml", "res/values-si-rLK/values-si-rLK.xml", "res/values-sv/values-sv.xml", "res/anim/abc_popup_exit.xml", "res/anim/abc_slide_out_top.xml", "res/anim/abc_grow_fade_in_from_bottom.xml", "res/anim/abc_popup_enter.xml", "res/anim/abc_slide_in_bottom.xml", "res/anim/abc_shrink_fade_out_from_bottom.xml", "res/anim/abc_slide_in_top.xml", "res/anim/abc_fade_out.xml", "res/anim/abc_slide_out_bottom.xml", "res/anim/abc_fade_in.xml", "res/values-my-rMM/values-my-rMM.xml", "res/values-b+sr+Latn/values-b+sr+Latn.xml", "res/values-fr/values-fr.xml", "res/values-uz-rUZ/values-uz-rUZ.xml", "res/values-ms-rMY/values-ms-rMY.xml", "res/values-ka-rGE/values-ka-rGE.xml", "res/values-fi/values-fi.xml", "res/values-fr-rCA/values-fr-rCA.xml", "res/values-v17/values-v17.xml", "res/values-ca/values-ca.xml", "res/values-in/values-in.xml", "res/values-vi/values-vi.xml", "res/drawable-ldrtl-hdpi-v17/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-ldrtl-hdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-ldrtl-hdpi-v17/abc_ic_menu_cut_mtrl_alpha.png", "res/layout/notification_media_cancel_action.xml", "res/layout/select_dialog_item_material.xml", "res/layout/notification_template_custom_big.xml", "res/layout/abc_action_bar_up_container.xml", "res/layout/abc_dialog_title_material.xml", "res/layout/notification_template_big_media_narrow.xml", "res/layout/abc_select_dialog_material.xml", "res/layout/abc_list_menu_item_layout.xml", "res/layout/notification_template_part_chronometer.xml", "res/layout/abc_popup_menu_header_item_layout.xml", "res/layout/notification_template_lines_media.xml", "res/layout/notification_template_part_time.xml", "res/layout/abc_list_menu_item_icon.xml", "res/layout/notification_action_tombstone.xml", "res/layout/abc_action_mode_bar.xml", "res/layout/notification_template_media_custom.xml", "res/layout/abc_popup_menu_item_layout.xml", "res/layout/abc_action_menu_item_layout.xml", "res/layout/abc_activity_chooser_view_list_item.xml", "res/layout/abc_list_menu_item_radio.xml", "res/layout/abc_screen_content_include.xml", "res/layout/abc_activity_chooser_view.xml", "res/layout/abc_alert_dialog_button_bar_material.xml", "res/layout/abc_action_menu_layout.xml", "res/layout/notification_template_big_media_narrow_custom.xml", "res/layout/notification_template_big_media_custom.xml", "res/layout/abc_list_menu_item_checkbox.xml", "res/layout/notification_media_action.xml", "res/layout/notification_template_icon_group.xml", "res/layout/abc_screen_simple.xml", "res/layout/abc_screen_simple_overlay_action_mode.xml", "res/layout/abc_screen_toolbar.xml", "res/layout/abc_alert_dialog_material.xml", "res/layout/abc_expanded_menu_layout.xml", "res/layout/abc_search_view.xml", "res/layout/notification_template_big_media.xml", "res/layout/abc_search_dropdown_item_icons_2line.xml", "res/layout/abc_action_bar_view_list_nav_layout.xml", "res/layout/select_dialog_multichoice_material.xml", "res/layout/abc_action_mode_close_item_material.xml", "res/layout/notification_template_media.xml", "res/layout/support_simple_spinner_dropdown_item.xml", "res/layout/notification_action.xml", "res/layout/abc_action_bar_title_item.xml", "res/layout/select_dialog_singlechoice_material.xml", "res/values-en-rGB/values-en-rGB.xml", "res/values-ja/values-ja.xml", "res/values-zh-rCN/values-zh-rCN.xml", "res/values-am/values-am.xml", "res/values-sq-rAL/values-sq-rAL.xml", "res/values-v16/values-v16.xml", "res/values-sl/values-sl.xml", "res/values-de/values-de.xml", "res/values-kk-rKZ/values-kk-rKZ.xml", "res/drawable-xxxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "res/drawable-xxxhdpi-v4/abc_ic_star_black_36dp.png", "res/drawable-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_light.png", "res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "res/drawable-xxxhdpi-v4/abc_text_select_handle_left_mtrl_dark.png", "res/drawable-xxxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "res/drawable-xxxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "res/drawable-xxxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "res/drawable-xxxhdpi-v4/abc_ic_star_black_48dp.png", "res/drawable-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_dark.png", "res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "res/drawable-xxxhdpi-v4/abc_ic_star_half_black_16dp.png", "res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "res/drawable-xxxhdpi-v4/abc_ic_star_half_black_36dp.png", "res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_light.png", "res/drawable-xxxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "res/drawable-xxxhdpi-v4/abc_ic_star_black_16dp.png", "res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "res/drawable-xxxhdpi-v4/abc_ic_star_half_black_48dp.png", "res/drawable-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "res/values-en-rAU/values-en-rAU.xml", "res/values-en-rIN/values-en-rIN.xml", "res/values-zh-rTW/values-zh-rTW.xml", "res/values-sw/values-sw.xml", "res/values-nb/values-nb.xml", "res/values-h720dp-v13/values-h720dp-v13.xml", "res/values-bs-rBA/values-bs-rBA.xml", "res/values-tr/values-tr.xml", "res/drawable-ldrtl-mdpi-v17/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-ldrtl-mdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-ldrtl-mdpi-v17/abc_ic_menu_cut_mtrl_alpha.png", "res/drawable-v23/abc_control_background_material.xml", "res/values-kn-rIN/values-kn-rIN.xml", "res/values-hy-rAM/values-hy-rAM.xml", "res/values-sw600dp-v13/values-sw600dp-v13.xml", "res/values-es-rUS/values-es-rUS.xml", "res/values-land/values-land.xml", "res/values-zh-rHK/values-zh-rHK.xml", "res/drawable-ldrtl-xxhdpi-v17/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-ldrtl-xxhdpi-v17/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-ldrtl-xxhdpi-v17/abc_ic_menu_cut_mtrl_alpha.png", "res/values-large-v4/values-large-v4.xml", "res/values-ko/values-ko.xml", "res/values-pt-rPT/values-pt-rPT.xml", "res/values-iw/values-iw.xml", "res/values-lt/values-lt.xml", "res/values-ro/values-ro.xml", "res/values-v14/values-v14.xml", "res/values-pt/values-pt.xml", "res/layout-v16/notification_template_custom_big.xml", "res/values-ar/values-ar.xml", "res/color-v23/abc_tint_seek_thumb.xml", "res/color-v23/abc_tint_btn_checkable.xml", "res/color-v23/abc_tint_edittext.xml", "res/color-v23/abc_tint_switch_thumb.xml", "res/color-v23/abc_btn_colored_borderless_text_material.xml", "res/color-v23/abc_tint_default.xml", "res/color-v23/abc_color_highlight_material.xml", "res/color-v23/abc_tint_spinner.xml", "res/color-v23/abc_tint_switch_track.xml", "res/values-hdpi-v4/values-hdpi-v4.xml", "res/values-ur-rPK/values-ur-rPK.xml", "res/values-ml-rIN/values-ml-rIN.xml", "res/drawable-xhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "res/drawable-xhdpi-v4/abc_list_pressed_holo_light.9.png", "res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "res/drawable-xhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "res/drawable-xhdpi-v4/abc_list_focused_holo.9.png", "res/drawable-xhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "res/drawable-xhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_popup_background_mtrl_mult.9.png", "res/drawable-xhdpi-v4/abc_ic_star_black_36dp.png", "res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "res/drawable-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-xhdpi-v4/abc_text_select_handle_left_mtrl_light.png", "res/drawable-xhdpi-v4/abc_list_longpressed_holo.9.png", "res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "res/drawable-xhdpi-v4/abc_list_pressed_holo_dark.9.png", "res/drawable-xhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "res/drawable-xhdpi-v4/abc_text_select_handle_left_mtrl_dark.png", "res/drawable-xhdpi-v4/abc_text_select_handle_middle_mtrl_light.png", "res/drawable-xhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "res/drawable-xhdpi-v4/notification_bg_low_normal.9.png", "res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "res/drawable-xhdpi-v4/abc_text_select_handle_middle_mtrl_dark.png", "res/drawable-xhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_ic_star_black_48dp.png", "res/drawable-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-xhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_text_select_handle_right_mtrl_dark.png", "res/drawable-xhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "res/drawable-xhdpi-v4/abc_ic_star_half_black_16dp.png", "res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "res/drawable-xhdpi-v4/notification_bg_normal.9.png", "res/drawable-xhdpi-v4/abc_ic_star_half_black_36dp.png", "res/drawable-xhdpi-v4/notify_panel_notification_icon_bg.png", "res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_text_select_handle_right_mtrl_light.png", "res/drawable-xhdpi-v4/notification_bg_low_pressed.9.png", "res/drawable-xhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/notification_bg_normal_pressed.9.png", "res/drawable-xhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "res/drawable-xhdpi-v4/abc_ic_star_black_16dp.png", "res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "res/drawable-xhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "res/drawable-xhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "res/drawable-xhdpi-v4/abc_ic_star_half_black_48dp.png", "res/drawable-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "res/values/values.xml", "res/values-hr/values-hr.xml", "res/values-mn-rMN/values-mn-rMN.xml", "res/values-da/values-da.xml", "res/values-hi/values-hi.xml", "res/values-it/values-it.xml", "res/color-v11/abc_background_cache_hint_selector_material_light.xml", "res/color-v11/abc_background_cache_hint_selector_material_dark.xml", "res/values-night-v8/values-night-v8.xml", "res/values-es/values-es.xml", "res/values-sk/values-sk.xml", "res/layout-v21/notification_template_custom_big.xml", "res/layout-v21/notification_action_tombstone.xml", "res/layout-v21/notification_template_icon_group.xml", "res/layout-v21/notification_action.xml", "res/values-lv/values-lv.xml", "res/values-ky-rKG/values-ky-rKG.xml", "res/values-v25/values-v25.xml", "res/values-hu/values-hu.xml", "res/drawable-xxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "res/drawable-xxhdpi-v4/abc_list_pressed_holo_light.9.png", "res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "res/drawable-xxhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "res/drawable-xxhdpi-v4/abc_list_focused_holo.9.png", "res/drawable-xxhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "res/drawable-xxhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_popup_background_mtrl_mult.9.png", "res/drawable-xxhdpi-v4/abc_ic_star_black_36dp.png", "res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "res/drawable-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_left_mtrl_light.png", "res/drawable-xxhdpi-v4/abc_list_longpressed_holo.9.png", "res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "res/drawable-xxhdpi-v4/abc_list_pressed_holo_dark.9.png", "res/drawable-xxhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_left_mtrl_dark.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_middle_mtrl_light.png", "res/drawable-xxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_middle_mtrl_dark.png", "res/drawable-xxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_ic_star_black_48dp.png", "res/drawable-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "res/drawable-xxhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_right_mtrl_dark.png", "res/drawable-xxhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png", "res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "res/drawable-xxhdpi-v4/abc_ic_star_half_black_36dp.png", "res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_text_select_handle_right_mtrl_light.png", "res/drawable-xxhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "res/drawable-xxhdpi-v4/abc_ic_star_black_16dp.png", "res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "res/drawable-xxhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "res/drawable-xxhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "res/drawable-xxhdpi-v4/abc_ic_star_half_black_48dp.png", "res/drawable-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "res/color/abc_search_url_text.xml", "res/color/switch_thumb_material_dark.xml", "res/color/abc_hint_foreground_material_light.xml", "res/color/abc_secondary_text_material_dark.xml", "res/color/abc_tint_seek_thumb.xml", "res/color/abc_tint_btn_checkable.xml", "res/color/abc_tint_edittext.xml", "res/color/abc_hint_foreground_material_dark.xml", "res/color/switch_thumb_material_light.xml", "res/color/abc_tint_switch_thumb.xml", "res/color/abc_btn_colored_borderless_text_material.xml", "res/color/abc_primary_text_material_light.xml", "res/color/abc_background_cache_hint_selector_material_light.xml", "res/color/abc_secondary_text_material_light.xml", "res/color/abc_primary_text_disable_only_material_light.xml", "res/color/abc_primary_text_disable_only_material_dark.xml", "res/color/abc_tint_default.xml", "res/color/abc_tint_spinner.xml", "res/color/abc_tint_switch_track.xml", "res/color/abc_background_cache_hint_selector_material_dark.xml", "res/color/abc_primary_text_material_dark.xml", "res/values-v12/values-v12.xml", "res/values-th/values-th.xml", "res/values-cs/values-cs.xml", "res/values-sr/values-sr.xml", "res/values-v13/values-v13.xml", "res/values-tl/values-tl.xml", "res/values-te-rIN/values-te-rIN.xml", "res/values-ne-rNP/values-ne-rNP.xml", "res/values-ta-rIN/values-ta-rIN.xml", "res/values-ldltr-v21/values-ldltr-v21.xml", "res/drawable/abc_switch_thumb_material.xml", "res/drawable/abc_btn_borderless_material.xml", "res/drawable/abc_textfield_search_material.xml", "res/drawable/abc_seekbar_track_material.xml", "res/drawable/abc_btn_colored_material.xml", "res/drawable/abc_ratingbar_indicator_material.xml", "res/drawable/notification_tile_bg.xml", "res/drawable/abc_ic_ab_back_material.xml", "res/drawable/notification_icon_background.xml", "res/drawable/notification_bg_low.xml", "res/drawable/abc_btn_default_mtrl_shape.xml", "res/drawable/abc_seekbar_thumb_material.xml", "res/drawable/abc_cab_background_internal_bg.xml", "res/drawable/abc_dialog_material_background.xml", "res/drawable/abc_text_cursor_material.xml", "res/drawable/notification_bg.xml", "res/drawable/abc_btn_radio_material.xml", "res/drawable/abc_item_background_holo_dark.xml", "res/drawable/abc_list_selector_holo_light.xml", "res/drawable/abc_ic_voice_search_api_material.xml", "res/drawable/abc_ic_search_api_material.xml", "res/drawable/abc_cab_background_top_material.xml", "res/drawable/abc_list_selector_background_transition_holo_dark.xml", "res/drawable/abc_edit_text_material.xml", "res/drawable/abc_tab_indicator_material.xml", "res/drawable/abc_ratingbar_material.xml", "res/drawable/abc_seekbar_tick_mark_material.xml", "res/drawable/abc_ic_go_search_api_material.xml", "res/drawable/abc_list_selector_holo_dark.xml", "res/drawable/abc_ic_arrow_drop_right_black_24dp.xml", "res/drawable/abc_btn_check_material.xml", "res/drawable/abc_spinner_textfield_background_material.xml", "res/drawable/abc_ic_clear_material.xml", "res/drawable/abc_item_background_holo_light.xml", "res/drawable/abc_list_selector_background_transition_holo_light.xml", "res/drawable/abc_ic_menu_overflow_material.xml", "res/drawable/abc_vector_test.xml", "res/drawable/abc_ratingbar_small_material.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_gridlayout_java.info b/build/secondary/third_party/android_tools/android_support_v7_gridlayout_java.info
new file mode 100644
index 0000000..28c8330
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_gridlayout_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_mediarouter_java.info b/build/secondary/third_party/android_tools/android_support_v7_mediarouter_java.info
new file mode 100644
index 0000000..55b51f5
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_mediarouter_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values-km-rKH/values-km-rKH.xml", "res/values-nl/values-nl.xml", "res/values-az-rAZ/values-az-rAZ.xml", "res/values-gl-rES/values-gl-rES.xml", "res/values-bg/values-bg.xml", "res/values-pt-rBR/values-pt-rBR.xml", "res/values-eu-rES/values-eu-rES.xml", "res/values-el/values-el.xml", "res/values-sw720dp-v13/values-sw720dp-v13.xml", "res/values-mk-rMK/values-mk-rMK.xml", "res/values-af/values-af.xml", "res/values-uk/values-uk.xml", "res/values-zu/values-zu.xml", "res/values-et-rEE/values-et-rEE.xml", "res/values-lo-rLA/values-lo-rLA.xml", "res/drawable-hdpi-v4/ic_mr_button_disconnected_light.png", "res/drawable-hdpi-v4/ic_vol_type_speaker_group_light.png", "res/drawable-hdpi-v4/ic_audiotrack_light.png", "res/drawable-hdpi-v4/ic_vol_type_speaker_light.png", "res/drawable-hdpi-v4/ic_vol_type_tv_dark.png", "res/drawable-hdpi-v4/ic_dialog_close_dark.png", "res/drawable-hdpi-v4/ic_vol_type_tv_light.png", "res/drawable-hdpi-v4/ic_mr_button_disabled_dark.png", "res/drawable-hdpi-v4/ic_mr_button_disabled_light.png", "res/drawable-hdpi-v4/ic_dialog_close_light.png", "res/drawable-hdpi-v4/ic_mr_button_disconnected_dark.png", "res/drawable-hdpi-v4/ic_vol_type_speaker_group_dark.png", "res/drawable-hdpi-v4/ic_vol_type_speaker_dark.png", "res/drawable-hdpi-v4/ic_media_pause_light.png", "res/drawable-hdpi-v4/ic_mr_button_grey.png", "res/drawable-hdpi-v4/ic_audiotrack_dark.png", "res/drawable-hdpi-v4/ic_media_play_light.png", "res/drawable-hdpi-v4/ic_media_pause_dark.png", "res/drawable-hdpi-v4/ic_media_play_dark.png", "res/values-bn-rBD/values-bn-rBD.xml", "res/values-is-rIS/values-is-rIS.xml", "res/values-gu-rIN/values-gu-rIN.xml", "res/drawable-mdpi-v4/ic_mr_button_disconnected_light.png", "res/drawable-mdpi-v4/ic_vol_type_speaker_group_light.png", "res/drawable-mdpi-v4/ic_audiotrack_light.png", "res/drawable-mdpi-v4/ic_vol_type_speaker_light.png", "res/drawable-mdpi-v4/ic_vol_type_tv_dark.png", "res/drawable-mdpi-v4/ic_dialog_close_dark.png", "res/drawable-mdpi-v4/ic_vol_type_tv_light.png", "res/drawable-mdpi-v4/ic_mr_button_disabled_dark.png", "res/drawable-mdpi-v4/ic_mr_button_disabled_light.png", "res/drawable-mdpi-v4/ic_dialog_close_light.png", "res/drawable-mdpi-v4/ic_mr_button_disconnected_dark.png", "res/drawable-mdpi-v4/ic_vol_type_speaker_group_dark.png", "res/drawable-mdpi-v4/ic_vol_type_speaker_dark.png", "res/drawable-mdpi-v4/ic_media_pause_light.png", "res/drawable-mdpi-v4/ic_mr_button_grey.png", "res/drawable-mdpi-v4/ic_audiotrack_dark.png", "res/drawable-mdpi-v4/ic_media_play_light.png", "res/drawable-mdpi-v4/ic_media_pause_dark.png", "res/drawable-mdpi-v4/ic_media_play_dark.png", "res/values-pa-rIN/values-pa-rIN.xml", "res/values-mr-rIN/values-mr-rIN.xml", "res/values-ru/values-ru.xml", "res/values-pl/values-pl.xml", "res/values-fa/values-fa.xml", "res/values-be-rBY/values-be-rBY.xml", "res/values-si-rLK/values-si-rLK.xml", "res/values-sv/values-sv.xml", "res/values-my-rMM/values-my-rMM.xml", "res/values-b+sr+Latn/values-b+sr+Latn.xml", "res/interpolator/mr_linear_out_slow_in.xml", "res/interpolator/mr_fast_out_slow_in.xml", "res/values-fr/values-fr.xml", "res/values-uz-rUZ/values-uz-rUZ.xml", "res/values-ms-rMY/values-ms-rMY.xml", "res/values-ka-rGE/values-ka-rGE.xml", "res/values-fi/values-fi.xml", "res/values-fr-rCA/values-fr-rCA.xml", "res/values-ca/values-ca.xml", "res/values-in/values-in.xml", "res/values-vi/values-vi.xml", "res/layout/mr_controller_material_dialog_b.xml", "res/layout/mr_chooser_dialog.xml", "res/layout/mr_chooser_list_item.xml", "res/layout/mr_volume_control.xml", "res/layout/mr_playback_control.xml", "res/layout/mr_controller_volume_item.xml", "res/values-en-rGB/values-en-rGB.xml", "res/values-ja/values-ja.xml", "res/values-zh-rCN/values-zh-rCN.xml", "res/values-am/values-am.xml", "res/values-sq-rAL/values-sq-rAL.xml", "res/values-sl/values-sl.xml", "res/values-de/values-de.xml", "res/values-kk-rKZ/values-kk-rKZ.xml", "res/drawable-xxxhdpi-v4/ic_group_collapse_03.png", "res/drawable-xxxhdpi-v4/ic_group_expand_15.png", "res/drawable-xxxhdpi-v4/ic_group_expand_13.png", "res/drawable-xxxhdpi-v4/ic_group_expand_10.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_14.png", "res/drawable-xxxhdpi-v4/ic_group_expand_06.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_11.png", "res/drawable-xxxhdpi-v4/ic_group_expand_11.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_01.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_10.png", "res/drawable-xxxhdpi-v4/ic_group_expand_07.png", "res/drawable-xxxhdpi-v4/ic_group_expand_09.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_13.png", "res/drawable-xxxhdpi-v4/ic_group_expand_01.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_00.png", "res/drawable-xxxhdpi-v4/ic_group_expand_00.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_04.png", "res/drawable-xxxhdpi-v4/ic_group_expand_12.png", "res/drawable-xxxhdpi-v4/ic_group_expand_08.png", "res/drawable-xxxhdpi-v4/ic_group_expand_04.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_08.png", "res/drawable-xxxhdpi-v4/ic_group_expand_03.png", "res/drawable-xxxhdpi-v4/ic_group_expand_02.png", "res/drawable-xxxhdpi-v4/ic_group_expand_05.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_02.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_07.png", "res/drawable-xxxhdpi-v4/ic_mr_button_grey.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_05.png", "res/drawable-xxxhdpi-v4/ic_group_expand_14.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_12.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_06.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_09.png", "res/drawable-xxxhdpi-v4/ic_group_collapse_15.png", "res/values-en-rAU/values-en-rAU.xml", "res/values-en-rIN/values-en-rIN.xml", "res/values-zh-rTW/values-zh-rTW.xml", "res/values-sw/values-sw.xml", "res/values-nb/values-nb.xml", "res/values-bs-rBA/values-bs-rBA.xml", "res/values-tr/values-tr.xml", "res/values-kn-rIN/values-kn-rIN.xml", "res/values-hy-rAM/values-hy-rAM.xml", "res/values-sw600dp-v13/values-sw600dp-v13.xml", "res/values-es-rUS/values-es-rUS.xml", "res/values-land/values-land.xml", "res/values-zh-rHK/values-zh-rHK.xml", "res/values-ko/values-ko.xml", "res/values-pt-rPT/values-pt-rPT.xml", "res/values-iw/values-iw.xml", "res/values-lt/values-lt.xml", "res/values-ro/values-ro.xml", "res/values-pt/values-pt.xml", "res/values-ar/values-ar.xml", "res/values-ur-rPK/values-ur-rPK.xml", "res/values-ml-rIN/values-ml-rIN.xml", "res/drawable-xhdpi-v4/ic_mr_button_connected_20_light.png", "res/drawable-xhdpi-v4/ic_mr_button_disconnected_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_06_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_14_dark.png", "res/drawable-xhdpi-v4/ic_vol_type_speaker_group_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_18_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_12_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_15_dark.png", "res/drawable-xhdpi-v4/ic_audiotrack_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_01_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_18_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_04_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_04_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_09_dark.png", "res/drawable-xhdpi-v4/ic_vol_type_speaker_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_03_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_19_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_15_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_17_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_20_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_07_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_12_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_19_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_11_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_22_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_10_light.png", "res/drawable-xhdpi-v4/ic_vol_type_tv_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_13_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_16_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_08_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_07_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_05_light.png", "res/drawable-xhdpi-v4/ic_dialog_close_dark.png", "res/drawable-xhdpi-v4/ic_vol_type_tv_light.png", "res/drawable-xhdpi-v4/ic_mr_button_disabled_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_disabled_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_13_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_00_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_09_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_02_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_02_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_21_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_21_light.png", "res/drawable-xhdpi-v4/ic_dialog_close_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_15_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_13_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_07_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_17_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_21_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_01_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_19_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_02_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_02_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_09_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_18_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_05_light.png", "res/drawable-xhdpi-v4/ic_mr_button_disconnected_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_10_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_14_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_00_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_03_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_15_light.png", "res/drawable-xhdpi-v4/ic_vol_type_speaker_group_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_06_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_11_light.png", "res/drawable-xhdpi-v4/ic_vol_type_speaker_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_10_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_16_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_08_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_09_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_11_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_05_dark.png", "res/drawable-xhdpi-v4/ic_media_pause_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_10_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_22_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_17_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_grey.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_08_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_04_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_16_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_20_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_03_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_14_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_17_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_06_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_06_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_20_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_01_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_16_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_21_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_12_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_12_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_22_light.png", "res/drawable-xhdpi-v4/ic_audiotrack_dark.png", "res/drawable-xhdpi-v4/ic_media_play_light.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_19_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_05_dark.png", "res/drawable-xhdpi-v4/ic_media_pause_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_22_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_14_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_11_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_00_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_08_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_13_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_18_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connecting_04_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_07_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_00_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_01_light.png", "res/drawable-xhdpi-v4/ic_media_play_dark.png", "res/drawable-xhdpi-v4/ic_mr_button_connected_03_light.png", "res/values/values.xml", "res/values-hr/values-hr.xml", "res/values-mn-rMN/values-mn-rMN.xml", "res/values-da/values-da.xml", "res/values-hi/values-hi.xml", "res/values-it/values-it.xml", "res/values-es/values-es.xml", "res/values-sk/values-sk.xml", "res/values-lv/values-lv.xml", "res/values-ky-rKG/values-ky-rKG.xml", "res/values-hu/values-hu.xml", "res/drawable-xxhdpi-v4/ic_mr_button_connected_20_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_disconnected_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_06_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_14_dark.png", "res/drawable-xxhdpi-v4/ic_vol_type_speaker_group_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_18_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_12_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_15_dark.png", "res/drawable-xxhdpi-v4/ic_audiotrack_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_01_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_18_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_04_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_04_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_09_dark.png", "res/drawable-xxhdpi-v4/ic_vol_type_speaker_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_03_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_19_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_15_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_17_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_20_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_07_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_12_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_19_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_11_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_22_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_10_light.png", "res/drawable-xxhdpi-v4/ic_vol_type_tv_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_13_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_16_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_08_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_07_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_05_light.png", "res/drawable-xxhdpi-v4/ic_dialog_close_dark.png", "res/drawable-xxhdpi-v4/ic_vol_type_tv_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_disabled_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_disabled_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_13_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_00_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_09_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_02_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_02_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_21_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_21_light.png", "res/drawable-xxhdpi-v4/ic_dialog_close_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_15_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_13_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_07_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_17_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_21_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_01_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_19_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_02_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_02_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_09_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_18_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_05_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_disconnected_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_10_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_14_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_00_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_03_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_15_light.png", "res/drawable-xxhdpi-v4/ic_vol_type_speaker_group_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_06_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_11_light.png", "res/drawable-xxhdpi-v4/ic_vol_type_speaker_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_10_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_16_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_08_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_09_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_11_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_05_dark.png", "res/drawable-xxhdpi-v4/ic_media_pause_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_10_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_22_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_17_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_grey.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_08_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_04_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_16_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_20_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_03_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_14_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_17_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_06_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_06_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_20_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_01_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_16_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_21_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_12_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_12_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_22_light.png", "res/drawable-xxhdpi-v4/ic_audiotrack_dark.png", "res/drawable-xxhdpi-v4/ic_media_play_light.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_19_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_05_dark.png", "res/drawable-xxhdpi-v4/ic_media_pause_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_22_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_14_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_11_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_00_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_08_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_13_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_18_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connecting_04_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_07_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_00_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_01_light.png", "res/drawable-xxhdpi-v4/ic_media_play_dark.png", "res/drawable-xxhdpi-v4/ic_mr_button_connected_03_light.png", "res/values-th/values-th.xml", "res/values-cs/values-cs.xml", "res/values-sr/values-sr.xml", "res/values-tl/values-tl.xml", "res/values-te-rIN/values-te-rIN.xml", "res/values-ne-rNP/values-ne-rNP.xml", "res/values-ta-rIN/values-ta-rIN.xml", "res/drawable/mr_button_connecting_light.xml", "res/drawable/mr_group_collapse.xml", "res/drawable/mr_button_connecting_dark.xml", "res/drawable/mr_dialog_close_dark.xml", "res/drawable/mr_vol_type_audiotrack_dark.xml", "res/drawable/mr_media_pause_light.xml", "res/drawable/mr_group_expand.xml", "res/drawable/mr_vol_type_audiotrack_light.xml", "res/drawable/mr_dialog_material_background_light.xml", "res/drawable/mr_media_play_dark.xml", "res/drawable/mr_button_dark.xml", "res/drawable/mr_media_pause_dark.xml", "res/drawable/mr_dialog_material_background_dark.xml", "res/drawable/mr_button_connected_light.xml", "res/drawable/mr_dialog_close_light.xml", "res/drawable/mr_media_play_light.xml", "res/drawable/mr_button_connected_dark.xml", "res/drawable/mr_button_light.xml" ]
+subjar_tuples = [ [ "internal_impl_25.0.1", "libs/internal_impl-25.0.1.jar" ] ]
+subjars = [ "libs/internal_impl-25.0.1.jar" ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_palette_java.info b/build/secondary/third_party/android_tools/android_support_v7_palette_java.info
new file mode 100644
index 0000000..a2ebd4a
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_palette_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_preference_java.info b/build/secondary/third_party/android_tools/android_support_v7_preference_java.info
new file mode 100644
index 0000000..fc60261
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_preference_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values-v17/values-v17.xml", "res/layout/preference_dropdown.xml", "res/layout/preference_list_fragment.xml", "res/layout/preference.xml", "res/layout/preference_category.xml", "res/layout/preference_widget_checkbox.xml", "res/layout/preference_widget_switch_compat.xml", "res/layout/preference_information.xml", "res/layout/preference_recyclerview.xml", "res/layout/preference_dialog_edittext.xml", "res/values/values.xml", "res/layout-v11/preference_dropdown.xml", "res/layout-v11/preference.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_v7_recyclerview_java.info b/build/secondary/third_party/android_tools/android_support_v7_recyclerview_java.info
new file mode 100644
index 0000000..a25d255
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_v7_recyclerview_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/android_support_vector_drawable_java.info b/build/secondary/third_party/android_tools/android_support_vector_drawable_java.info
new file mode 100644
index 0000000..a2ebd4a
--- /dev/null
+++ b/build/secondary/third_party/android_tools/android_support_vector_drawable_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_auth_base_java.info b/build/secondary/third_party/android_tools/google_play_services_auth_base_java.info
new file mode 100644
index 0000000..0dfcb237
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_auth_base_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_auth_java.info b/build/secondary/third_party/android_tools/google_play_services_auth_java.info
new file mode 100644
index 0000000..acf40e8
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_auth_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = false
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_base_java.info b/build/secondary/third_party/android_tools/google_play_services_base_java.info
new file mode 100644
index 0000000..0a0aeb4
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_base_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = false
+resources = [ "res/color/common_google_signin_btn_text_dark.xml", "res/color/common_google_signin_btn_text_light.xml", "res/color/common_google_signin_btn_tint.xml", "res/drawable-hdpi-v4/common_full_open_on_phone.png", "res/drawable-hdpi-v4/common_google_signin_btn_icon_dark_normal_background.9.png", "res/drawable-hdpi-v4/common_google_signin_btn_icon_light_normal_background.9.png", "res/drawable-hdpi-v4/common_google_signin_btn_text_dark_normal_background.9.png", "res/drawable-hdpi-v4/common_google_signin_btn_text_light_normal_background.9.png", "res/drawable-hdpi-v4/googleg_disabled_color_18.png", "res/drawable-hdpi-v4/googleg_standard_color_18.png", "res/drawable-mdpi-v4/common_google_signin_btn_icon_dark_normal_background.9.png", "res/drawable-mdpi-v4/common_google_signin_btn_icon_light_normal_background.9.png", "res/drawable-mdpi-v4/common_google_signin_btn_text_dark_normal_background.9.png", "res/drawable-mdpi-v4/common_google_signin_btn_text_light_normal_background.9.png", "res/drawable-mdpi-v4/googleg_disabled_color_18.png", "res/drawable-mdpi-v4/googleg_standard_color_18.png", "res/drawable-xhdpi-v4/common_full_open_on_phone.png", "res/drawable-xhdpi-v4/common_google_signin_btn_icon_dark_normal_background.9.png", "res/drawable-xhdpi-v4/common_google_signin_btn_icon_light_normal_background.9.png", "res/drawable-xhdpi-v4/common_google_signin_btn_text_dark_normal_background.9.png", "res/drawable-xhdpi-v4/common_google_signin_btn_text_light_normal_background.9.png", "res/drawable-xhdpi-v4/googleg_disabled_color_18.png", "res/drawable-xhdpi-v4/googleg_standard_color_18.png", "res/drawable-xxhdpi-v4/common_google_signin_btn_icon_dark_normal_background.9.png", "res/drawable-xxhdpi-v4/common_google_signin_btn_icon_light_normal_background.9.png", "res/drawable-xxhdpi-v4/common_google_signin_btn_text_dark_normal_background.9.png", "res/drawable-xxhdpi-v4/common_google_signin_btn_text_light_normal_background.9.png", "res/drawable-xxhdpi-v4/googleg_disabled_color_18.png", "res/drawable-xxhdpi-v4/googleg_standard_color_18.png", "res/drawable/common_google_signin_btn_icon_dark.xml", "res/drawable/common_google_signin_btn_icon_dark_focused.xml", "res/drawable/common_google_signin_btn_icon_dark_normal.xml", "res/drawable/common_google_signin_btn_icon_disabled.xml", "res/drawable/common_google_signin_btn_icon_light.xml", "res/drawable/common_google_signin_btn_icon_light_focused.xml", "res/drawable/common_google_signin_btn_icon_light_normal.xml", "res/drawable/common_google_signin_btn_text_dark.xml", "res/drawable/common_google_signin_btn_text_dark_focused.xml", "res/drawable/common_google_signin_btn_text_dark_normal.xml", "res/drawable/common_google_signin_btn_text_disabled.xml", "res/drawable/common_google_signin_btn_text_light.xml", "res/drawable/common_google_signin_btn_text_light_focused.xml", "res/drawable/common_google_signin_btn_text_light_normal.xml", "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar/values.xml", "res/values-az/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rGB/values.xml", "res/values-es-rUS/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gu/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mr/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_basement_java.info b/build/secondary/third_party/android_tools/google_play_services_basement_java.info
new file mode 100644
index 0000000..19c75ad
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_basement_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = true
+is_manifest_empty = false
+resources = [ "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar/values.xml", "res/values-az/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rGB/values.xml", "res/values-es-rUS/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gu/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mr/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_cast_java.info b/build/secondary/third_party/android_tools/google_play_services_cast_java.info
new file mode 100644
index 0000000..913e27b
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_cast_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [ "res/drawable-hdpi-v4/cast_ic_notification_0.png", "res/drawable-hdpi-v4/cast_ic_notification_1.png", "res/drawable-hdpi-v4/cast_ic_notification_2.png", "res/drawable-hdpi-v4/cast_ic_notification_on.png", "res/drawable-mdpi-v4/cast_ic_notification_0.png", "res/drawable-mdpi-v4/cast_ic_notification_1.png", "res/drawable-mdpi-v4/cast_ic_notification_2.png", "res/drawable-mdpi-v4/cast_ic_notification_on.png", "res/drawable-xhdpi-v4/cast_ic_notification_0.png", "res/drawable-xhdpi-v4/cast_ic_notification_1.png", "res/drawable-xhdpi-v4/cast_ic_notification_2.png", "res/drawable-xhdpi-v4/cast_ic_notification_on.png", "res/drawable-xxhdpi-v4/cast_ic_notification_0.png", "res/drawable-xxhdpi-v4/cast_ic_notification_1.png", "res/drawable-xxhdpi-v4/cast_ic_notification_2.png", "res/drawable-xxhdpi-v4/cast_ic_notification_on.png", "res/drawable/cast_ic_notification_connecting.xml", "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar/values.xml", "res/values-az/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rGB/values.xml", "res/values-es-rUS/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gu/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mr/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_gcm_java.info b/build/secondary/third_party/android_tools/google_play_services_gcm_java.info
new file mode 100644
index 0000000..acf40e8
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_gcm_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = false
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_iid_java.info b/build/secondary/third_party/android_tools/google_play_services_iid_java.info
new file mode 100644
index 0000000..acf40e8
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_iid_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = false
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_location_java.info b/build/secondary/third_party/android_tools/google_play_services_location_java.info
new file mode 100644
index 0000000..0dfcb237
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_location_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_nearby_java.info b/build/secondary/third_party/android_tools/google_play_services_nearby_java.info
new file mode 100644
index 0000000..acf40e8
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_nearby_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = false
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_tasks_java.info b/build/secondary/third_party/android_tools/google_play_services_tasks_java.info
new file mode 100644
index 0000000..0dfcb237
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_tasks_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/build/secondary/third_party/android_tools/google_play_services_vision_java.info b/build/secondary/third_party/android_tools/google_play_services_vision_java.info
new file mode 100644
index 0000000..0dfcb237
--- /dev/null
+++ b/build/secondary/third_party/android_tools/google_play_services_vision_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index 9837932..3ef88ede 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1188,12 +1188,13 @@
   }
 
   void WillPrepareTilesOnThread(LayerTreeHostImpl* host_impl) override {
-    if (host_impl->sync_tree()->source_frame_number() != 0)
+    // After activating the sync tree PrepareTiles will be called
+    // again (which races with the test exiting).
+    LayerTreeImpl* sync_tree = host_impl->sync_tree();
+    if (!sync_tree || TestEnded())
       return;
 
-    // After checking this on the sync tree, we will activate, which will cause
-    // PrepareTiles to happen again (which races with the test exiting).
-    if (TestEnded())
+    if (sync_tree->source_frame_number() != 0)
       return;
 
     scoped_refptr<AnimationTimeline> timeline_impl =
@@ -1201,7 +1202,7 @@
     scoped_refptr<AnimationPlayer> player_impl =
         timeline_impl->GetPlayerById(player_id_);
 
-    LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
+    LayerImpl* child = sync_tree->LayerById(layer_->id());
     Animation* animation = player_impl->GetAnimation(TargetProperty::TRANSFORM);
 
     // The animation should be starting for the first frame.
@@ -1225,13 +1226,8 @@
   scoped_refptr<Layer> layer_;
 };
 
-SINGLE_THREAD_TEST_F(LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit);
-
-// The multi-thread test is flaky. See http://crbug.com/755965.
-using DISABLED_LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit =
-    LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit;
-MULTI_THREAD_TEST_F(
-    DISABLED_LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit);
+SINGLE_AND_MULTI_THREAD_TEST_F(
+    LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit);
 
 // When a layer with an animation is removed from the tree and later re-added,
 // the animation should resume.
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4a08e51..802a7fa 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -189,6 +189,7 @@
     "$google_play_services_package:google_play_services_nearby_java",
     "$google_play_services_package:google_play_services_tasks_java",
     "//base:base_java",
+    "//chrome/android/third_party/widget_bottomsheet_base:widget_bottomsheet_base_java",
     "//chrome/android/webapk/libs/client:client_java",
     "//chrome/android/webapk/libs/common:common_java",
     "//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java",
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 96eb57bc0..755d158 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1581,7 +1581,7 @@
      * that the compositor's surface should support alpha and not be marked as opaque.
      */
     public void setOverlayMode(boolean useOverlayMode) {
-        mCompositorViewHolder.setOverlayMode(useOverlayMode);
+        if (mCompositorViewHolder != null) mCompositorViewHolder.setOverlayMode(useOverlayMode);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index bb6cd67a..51106db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -43,6 +43,7 @@
     public static final String ACCOUNT_PICKER_DIALOG_TAG = "account_picker_dialog_tag";
     public static final String EXTRA_SHOW_SEARCH_ENGINE_PICKER = "show_search_engine_picker";
 
+    private SignInPreference mSignInPreference;
     private ManagedPreferenceDelegate mManagedPreferenceDelegate;
 
     public MainPreferences() {
@@ -64,6 +65,7 @@
 
         if (SigninManager.get(getActivity()).isSigninSupported()) {
             SigninManager.get(getActivity()).addSignInStateObserver(this);
+            setupSignInPref();
         }
     }
 
@@ -72,6 +74,7 @@
         super.onPause();
         if (SigninManager.get(getActivity()).isSigninSupported()) {
             SigninManager.get(getActivity()).removeSignInStateObserver(this);
+            clearSignInPref();
         }
     }
 
@@ -185,6 +188,18 @@
         pref.setSummary(getResources().getString(isOn ? R.string.text_on : R.string.text_off));
     }
 
+    private void setupSignInPref() {
+        mSignInPreference = (SignInPreference) findPreference(PREF_SIGN_IN);
+        mSignInPreference.registerForUpdates();
+    }
+
+    private void clearSignInPref() {
+        if (mSignInPreference != null) {
+            mSignInPreference.unregisterForUpdates();
+            mSignInPreference = null;
+        }
+    }
+
     // SignInStateObserver
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
index f396b75..9096a914 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
@@ -7,7 +7,6 @@
 import android.accounts.Account;
 import android.content.Context;
 import android.preference.Preference;
-import android.preference.PreferenceManager;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.view.View;
@@ -58,10 +57,10 @@
         mProfileDataCache = new ProfileDataCache(context, Profile.getLastUsedProfile(), imageSize);
     }
 
-    @Override
-    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
-        super.onAttachedToHierarchy(preferenceManager);
-
+    /**
+     * Starts listening for updates to the sign-in and sync state.
+     */
+    public void registerForUpdates() {
         AccountManagerFacade.get().addObserver(this);
         SigninManager.get(getContext()).addSignInAllowedObserver(this);
         mProfileDataCache.addObserver(this);
@@ -75,8 +74,11 @@
         update();
     }
 
-    @Override
-    protected void onPrepareForRemoval() {
+    /**
+     * Stops listening for updates to the sign-in and sync state. Every call to registerForUpdates()
+     * must be matched with a call to this method.
+     */
+    public void unregisterForUpdates() {
         AccountManagerFacade.get().removeObserver(this);
         SigninManager.get(getContext()).removeSignInAllowedObserver(this);
         mProfileDataCache.removeObserver(this);
@@ -85,8 +87,6 @@
         if (syncService != null) {
             syncService.removeSyncStateChangedListener(this);
         }
-
-        super.onPrepareForRemoval();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
index dedc40e6..ee80947 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
@@ -61,6 +61,11 @@
     Boolean isBackButtonEnabled();
 
     /**
+     * Returns whether the forward button is enabled.
+     */
+    Boolean isForwardButtonEnabled();
+
+    /**
      * Requests to exit VR.
      */
     void requestToExitVr(@UiUnsupportedMode int reason);
@@ -70,4 +75,14 @@
      * VrShell from the view hierarchy.
      */
     void onBeforeWindowDetached();
+
+    /**
+     *  Triggers VrShell to navigate forward.
+     */
+    void navigateForward();
+
+    /**
+     *  Triggers VrShell to navigate backward.
+     */
+    void navigateBack();
 }
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 3e6f790..536c0ac 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
@@ -650,6 +650,8 @@
                 UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI);
     }
 
+    @VisibleForTesting
+    @Override
     @CalledByNative
     public void navigateForward() {
         if (!mCanGoForward) return;
@@ -657,6 +659,8 @@
         updateHistoryButtonsVisibility();
     }
 
+    @VisibleForTesting
+    @Override
     @CalledByNative
     public void navigateBack() {
         if (!mCanGoBack) return;
@@ -720,6 +724,12 @@
     }
 
     @VisibleForTesting
+    @Override
+    public Boolean isForwardButtonEnabled() {
+        return mCanGoForward;
+    }
+
+    @VisibleForTesting
     public float getContentWidthForTesting() {
         return mLastContentWidth;
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
index 0b33c64..7602064 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
@@ -12,7 +12,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -76,11 +75,6 @@
         mAccountManager.addAccountHolderExplicitly(accountHolder.build());
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerFacade.resetAccountManagerFacadeForTests();
-    }
-
     /**
      * Launches the main preferences.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
index ddb81326..5b50ee92 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
@@ -290,7 +290,7 @@
     @MediumTest
     @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
     public void testBackDoesntBackgroundChrome()
-            throws IllegalArgumentException, InterruptedException, TimeoutException {
+            throws IllegalArgumentException, InterruptedException {
         Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
         mVrTestRule.loadUrlInNewTab(getUrl(Page.PAGE_2D), false, TabLaunchType.FROM_CHROME_UI);
         Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
@@ -305,4 +305,32 @@
         });
         Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
     }
+
+    /**
+     * Tests navigation from a fullscreened WebVR to a WebVR page.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
+    public void testNavigationButtons() throws IllegalArgumentException, InterruptedException {
+        Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
+        Assert.assertFalse(
+                "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled());
+        mVrTestRule.loadUrlInNewTab(getUrl(Page.PAGE_2D), false, TabLaunchType.FROM_CHROME_UI);
+        Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
+        Assert.assertFalse(
+                "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled());
+        mVrTestRule.loadUrl(getUrl(Page.PAGE_WEBVR));
+        Assert.assertTrue("Back button isn't enabled.", VrTransitionUtils.isBackButtonEnabled());
+        Assert.assertFalse(
+                "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled());
+        VrTransitionUtils.navigateBack();
+        Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled());
+        Assert.assertTrue(
+                "Forward button isn't enabled.", VrTransitionUtils.isForwardButtonEnabled());
+        VrTransitionUtils.navigateForward();
+        Assert.assertTrue("Back button isn't enabled.", VrTransitionUtils.isBackButtonEnabled());
+        Assert.assertFalse(
+                "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java
index bbe8292..ec99c13f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java
@@ -141,6 +141,45 @@
     }
 
     /**
+     * @return Whether the VR forward button is enabled.
+     */
+    public static Boolean isForwardButtonEnabled() {
+        final AtomicBoolean isForwardButtonEnabled = new AtomicBoolean();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                isForwardButtonEnabled.set(
+                        VrShellDelegate.getVrShellForTesting().isForwardButtonEnabled());
+            }
+        });
+        return isForwardButtonEnabled.get();
+    }
+
+    /**
+     * Navigates VrShell back.
+     */
+    public static void navigateBack() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                VrShellDelegate.getVrShellForTesting().navigateBack();
+            }
+        });
+    }
+
+    /**
+     * Navigates VrShell forward.
+     */
+    public static void navigateForward() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                VrShellDelegate.getVrShellForTesting().navigateForward();
+            }
+        });
+    }
+
+    /**
      * Sends an intent to Chrome telling it to autopresent the given URL. This
      * is expected to fail unless the trusted intent check is disabled in VrShellDelegate.
      *
diff --git a/chrome/android/third_party/widget_bottomsheet_base/BUILD.gn b/chrome/android/third_party/widget_bottomsheet_base/BUILD.gn
new file mode 100644
index 0000000..78bcb51
--- /dev/null
+++ b/chrome/android/third_party/widget_bottomsheet_base/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2017 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("widget_bottomsheet_base_java") {
+  java_files = [
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperBase.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperIcs.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationItemView.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenu.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenuView.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationPresenter.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationView.java",
+    "java/src/org/chromium/chrome/browser/widget/bottomsheet/base/ThemeUtils.java",
+  ]
+
+  deps = [
+    "//third_party/android_tools:android_support_annotations_java",
+    "//third_party/android_tools:android_support_design_java",
+    "//third_party/android_tools:android_support_transition_java",
+    "//third_party/android_tools:android_support_v7_appcompat_java",
+  ]
+}
diff --git a/chrome/android/third_party/widget_bottomsheet_base/README.chromium b/chrome/android/third_party/widget_bottomsheet_base/README.chromium
index dc2aa0a..1f06148 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/README.chromium
+++ b/chrome/android/third_party/widget_bottomsheet_base/README.chromium
@@ -6,4 +6,5 @@
 Bottom navigation menu widget from Android SDK Sources 25.
 
 Local Modifications:
-* None.
\ No newline at end of file
+* Change package.
+* Removed unnecessary version check when creating animation helper in BottomNavigationMenuView.
\ No newline at end of file
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperBase.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperBase.java
index 3d0e91b..5d1ea008 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperBase.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperBase.java
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package android.support.design.internal;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import android.view.ViewGroup;
 
+/**
+ * Forked from android.support.design.internal.BottomNavigationAnimationHelperBase.
+ */
 class BottomNavigationAnimationHelperBase {
     void beginDelayedTransition(ViewGroup view) {
         // Do nothing.
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperIcs.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperIcs.java
index e8766f472..bde2b91 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperIcs.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationAnimationHelperIcs.java
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-package android.support.design.internal;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
+import android.support.design.internal.TextScale;
 import android.support.transition.AutoTransition;
 import android.support.transition.TransitionManager;
 import android.support.transition.TransitionSet;
 import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.ViewGroup;
 
-class BottomNavigationAnimationHelperIcs extends BottomNavigationAnimationHelperBase {
+/** Forked from android.support.design.internal.BottomNavigationAnimationHelperIcs */
+public class BottomNavigationAnimationHelperIcs extends BottomNavigationAnimationHelperBase {
     private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
 
     private final TransitionSet mSet;
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationItemView.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationItemView.java
index c5ed684..c1af27a 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationItemView.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationItemView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.design.internal;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
@@ -38,6 +38,8 @@
 import android.widget.TextView;
 
 /**
+ * Forked from android.support.design.internal.BottomNavigationItemView.
+ *
  * @hide
  */
 @RestrictTo(GROUP_ID)
@@ -130,9 +132,9 @@
 
     @Override
     public void setChecked(boolean checked) {
-        ViewCompat.setPivotX(mLargeLabel, mLargeLabel.getWidth() / 2);
+        ViewCompat.setPivotX(mLargeLabel, mLargeLabel.getWidth() / 2f);
         ViewCompat.setPivotY(mLargeLabel, mLargeLabel.getBaseline());
-        ViewCompat.setPivotX(mSmallLabel, mSmallLabel.getWidth() / 2);
+        ViewCompat.setPivotX(mSmallLabel, mSmallLabel.getWidth() / 2f);
         ViewCompat.setPivotY(mSmallLabel, mSmallLabel.getBaseline());
         if (mShiftingMode) {
             if (checked) {
@@ -242,4 +244,4 @@
                 background == 0 ? null : ContextCompat.getDrawable(getContext(), background);
         ViewCompat.setBackground(this, backgroundDrawable);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenu.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenu.java
index eaf5ad4..8110478 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenu.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenu.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.design.internal;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
@@ -26,6 +26,8 @@
 import android.view.SubMenu;
 
 /**
+ * Forked from android.support.design.internal.BottomNavigationMenu.
+ *
  * @hide
  */
 @RestrictTo(GROUP_ID)
@@ -56,4 +58,4 @@
         startDispatchingItemsChanged();
         return item;
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenuView.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenuView.java
index d05c414b..27a630b 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenuView.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationMenuView.java
@@ -21,10 +21,11 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.os.Build;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.design.R;
+import android.support.design.internal.BottomNavigationItemView;
+import android.support.design.internal.BottomNavigationPresenter;
 import android.support.v4.util.Pools;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.view.menu.MenuBuilder;
@@ -82,7 +83,12 @@
         mOnClickListener = new OnClickListener() {
             @Override
             public void onClick(View v) {
-                final BottomNavigationItemView itemView = (BottomNavigationItemView) v;
+                BottomNavigationItemView itemView;
+                try {
+                    itemView = (BottomNavigationItemView) v;
+                } catch (ClassCastException e) {
+                    return;
+                }
                 final int itemPosition = itemView.getItemPosition();
                 if (!mMenu.performItemAction(itemView.getItemData(), mPresenter, 0)) {
                     activateNewButton(itemPosition);
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationPresenter.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationPresenter.java
index 6d020bf..37a52bc 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationPresenter.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationPresenter.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.design.internal;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import android.content.Context;
 import android.os.Parcelable;
@@ -29,6 +29,8 @@
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
 /**
+ * Forked from android.support.design.internal.BottomNavigationPresenter.
+ *
  * @hide
  */
 @RestrictTo(GROUP_ID)
@@ -104,4 +106,4 @@
     public void setUpdateSuspended(boolean updateSuspended) {
         mUpdateSuspended = updateSuspended;
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationView.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationView.java
index a6909e3..5539db2 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationView.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/BottomNavigationView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.design.widget;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -43,6 +43,8 @@
 import android.widget.FrameLayout;
 
 /**
+ * Forked from android.support.design.internal.BottomNavigationView.
+ *
  * <p>
  * Represents a standard bottom navigation bar for application. It is an implementation of
  * <a href="https://material.google.com/components/bottom-navigation.html">material design bottom
@@ -335,4 +337,4 @@
                 new int[] {baseColor.getColorForState(DISABLED_STATE_SET, defaultColor),
                         colorPrimary, defaultColor});
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/ThemeUtils.java b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/ThemeUtils.java
index 2f1cf405..98a2719 100644
--- a/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/ThemeUtils.java
+++ b/chrome/android/third_party/widget_bottomsheet_base/java/src/org/chromium/chrome/browser/widget/bottomsheet/base/ThemeUtils.java
@@ -14,22 +14,23 @@
  * limitations under the License.
  */
 
-package android.support.design.widget;
+package org.chromium.chrome.browser.widget.bottomsheet.base;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.design.R;
 
-class ThemeUtils {
+/**
+ * Forked from android.support.design.widget.ThemeUtils.
+ */
+public class ThemeUtils {
     private static final int[] APPCOMPAT_CHECK_ATTRS = {
             android.support.v7.appcompat.R.attr.colorPrimary};
 
     static void checkAppCompatTheme(Context context) {
         TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);
         final boolean failed = !a.hasValue(0);
-        if (a != null) {
-            a.recycle();
-        }
+        a.recycle();
         if (failed) {
             throw new IllegalArgumentException("You need to use a Theme.AppCompat theme "
                     + "(or descendant) with the design library.");
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 382535a..8bb443e 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -36,6 +36,9 @@
     <message name="IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_BETA" desc="The beta label in the message about current channel.">
       beta
     </message>
+    <message name="IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_CANARY" desc="The canary label in the message about current channel.">
+      canary
+    </message>
     <message name="IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_DEV" desc="The dev label in the message about current channel.">
       dev
     </message>
diff --git a/chrome/browser/android/history/browsing_history_bridge.cc b/chrome/browser/android/history/browsing_history_bridge.cc
index 03c27aa..11c81d8f5 100644
--- a/chrome/browser/android/history/browsing_history_bridge.cc
+++ b/chrome/browser/android/history/browsing_history_bridge.cc
@@ -108,7 +108,7 @@
 
   Java_BrowsingHistoryBridge_onQueryHistoryComplete(
       env, j_history_service_obj_, j_query_result_obj_,
-      !(query_results_info.reached_beginning));
+      !(query_results_info.reached_beginning_of_local));
 }
 
 void BrowsingHistoryBridge::MarkItemForRemoval(
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index e011d41..328da5d 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1539,12 +1539,6 @@
              NEEDS_TEST_SERVER);
 }
 
-// Very flaky on Mac: http://crbug.com/614377
-#ifdef OS_MAC
-#define MAYBE_Shim_TestDeclarativeWebRequestAPISendMessage DISABLED_Shim_TestDeclarativeWebRequestAPISendMessage
-#else
-#define MAYBE_Shim_TestDeclarativeWebRequestAPISendMessage Shim_TestDeclarativeWebRequestAPISendMessage
-#endif
 IN_PROC_BROWSER_TEST_P(WebViewTest,
                        Shim_TestDeclarativeWebRequestAPISendMessage) {
   TestHelper("testDeclarativeWebRequestAPISendMessage",
@@ -1552,6 +1546,13 @@
              NEEDS_TEST_SERVER);
 }
 
+IN_PROC_BROWSER_TEST_P(
+    WebViewTest,
+    Shim_TestDeclarativeWebRequestAPISendMessageSecondWebView) {
+  TestHelper("testDeclarativeWebRequestAPISendMessageSecondWebView",
+             "web_view/shim", NEEDS_TEST_SERVER);
+}
+
 IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPI) {
   TestHelper("testWebRequestAPI", "web_view/shim", NEEDS_TEST_SERVER);
 }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 3a6d7e7..be834deb 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -385,6 +385,8 @@
     "arc/notification/arc_boot_error_notification.h",
     "arc/notification/arc_provision_notification_service.cc",
     "arc/notification/arc_provision_notification_service.h",
+    "arc/oemcrypto/arc_oemcrypto_bridge.cc",
+    "arc/oemcrypto/arc_oemcrypto_bridge.h",
     "arc/optin/arc_optin_preference_handler.cc",
     "arc/optin/arc_optin_preference_handler.h",
     "arc/optin/arc_optin_preference_handler_observer.h",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 106e1a8..927945de 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/chromeos/arc/kiosk/arc_kiosk_bridge.h"
 #include "chrome/browser/chromeos/arc/notification/arc_boot_error_notification.h"
 #include "chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h"
+#include "chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/chromeos/arc/print/arc_print_service.h"
@@ -49,7 +50,6 @@
 #include "components/arc/metrics/arc_metrics_service.h"
 #include "components/arc/net/arc_net_host_impl.h"
 #include "components/arc/obb_mounter/arc_obb_mounter_bridge.h"
-#include "components/arc/oemcrypto/arc_oemcrypto_bridge.h"
 #include "components/arc/power/arc_power_bridge.h"
 #include "components/arc/storage_manager/arc_storage_manager.h"
 #include "components/arc/volume_mounter/arc_volume_mounter_bridge.h"
diff --git a/components/arc/oemcrypto/arc_oemcrypto_bridge.cc b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
similarity index 85%
rename from components/arc/oemcrypto/arc_oemcrypto_bridge.cc
rename to chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
index e0d1498..941af6c5 100644
--- a/components/arc/oemcrypto/arc_oemcrypto_bridge.cc
+++ b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/arc/oemcrypto/arc_oemcrypto_bridge.h"
+#include "chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.h"
 
 #include "base/bind.h"
 #include "base/memory/singleton.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chromeos/dbus/arc_oemcrypto_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/arc/arc_bridge_service.h"
@@ -84,6 +85,24 @@
 
 void ArcOemCryptoBridge::Connect(mojom::OemCryptoServiceRequest request) {
   DVLOG(1) << "ArcOemCryptoBridge::Connect called";
+
+  // Check that the user has Attestation for Content Protection enabled in
+  // their Chrome settings and if they do not then block this connection since
+  // OEMCrypto utilizes Attestation as the root of trust for its DRM
+  // implementation.
+  bool attestation_enabled = false;
+  if (!chromeos::CrosSettings::Get()->GetBoolean(
+          chromeos::kAttestationForContentProtectionEnabled,
+          &attestation_enabled)) {
+    LOG(ERROR) << "Failed to get attestation device setting";
+    return;
+  }
+  if (!attestation_enabled) {
+    DVLOG(1) << "OEMCrypto L1 DRM denied because Verified Access is disabled "
+                "for this device.";
+    return;
+  }
+
   if (oemcrypto_host_ptr_.is_bound()) {
     DVLOG(1) << "Re-using bootstrap connection for OemCryptoService Connect";
     oemcrypto_host_ptr_->Connect(std::move(request));
diff --git a/components/arc/oemcrypto/arc_oemcrypto_bridge.h b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.h
similarity index 89%
rename from components/arc/oemcrypto/arc_oemcrypto_bridge.h
rename to chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.h
index c15b6b1..ede2407c 100644
--- a/components/arc/oemcrypto/arc_oemcrypto_bridge.h
+++ b/chrome/browser/chromeos/arc/oemcrypto/arc_oemcrypto_bridge.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
-#define COMPONENTS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
 
 #include <stdint.h>
 
@@ -59,4 +59,4 @@
 
 }  // namespace arc
 
-#endif  // COMPONENTS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_OEMCRYPTO_ARC_OEMCRYPTO_BRIDGE_H_
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
index 533dd3d..c20d346 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
@@ -90,16 +90,25 @@
 }
 
 ChromeDataUseAscriber::DataUseRecorderEntry
-ChromeDataUseAscriber::GetOrCreateDataUseRecorderEntry(
-    net::URLRequest* request) {
+ChromeDataUseAscriber::GetDataUseRecorderEntry(net::URLRequest* request) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   // If a DataUseRecorder has already been set as user data, then return that.
   auto* user_data =
       static_cast<DataUseRecorderEntryAsUserData*>(request->GetUserData(
           DataUseRecorderEntryAsUserData::kDataUseAscriberUserDataKey));
-  if (user_data)
-    return user_data->recorder_entry();
+  return user_data ? user_data->recorder_entry() : data_use_recorders_.end();
+}
+
+ChromeDataUseAscriber::DataUseRecorderEntry
+ChromeDataUseAscriber::GetOrCreateDataUseRecorderEntry(
+    net::URLRequest* request) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // If a DataUseRecorder has already been created, then return that.
+  auto recorder = GetDataUseRecorderEntry(request);
+  if (recorder != data_use_recorders_.end())
+    return recorder;
 
   // If request is associated with a ChromeService, create a new
   // DataUseRecorder for it. There is no reason to aggregate URLRequests
@@ -217,10 +226,7 @@
   if (IsDisabledPlatform())
     return;
 
-  // TODO(rajendrant): GetDataUseRecorder is sufficient and
-  // GetOrCreateDataUseRecorderEntry is not needed. The entry gets created in
-  // DataUseAscriber::OnBeforeUrlRequest().
-  const DataUseRecorderEntry entry = GetOrCreateDataUseRecorderEntry(request);
+  const DataUseRecorderEntry entry = GetDataUseRecorderEntry(request);
 
   if (entry == data_use_recorders_.end())
     return;
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
index 7e629f4..dc649e0 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
@@ -161,6 +161,8 @@
     DISALLOW_COPY_AND_ASSIGN(MainRenderFrameEntry);
   };
 
+  DataUseRecorderEntry GetDataUseRecorderEntry(net::URLRequest* request);
+
   DataUseRecorderEntry GetOrCreateDataUseRecorderEntry(
       net::URLRequest* request);
 
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index dbd981a..109ef6a 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -310,6 +310,8 @@
       settings_private::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[::chromeos::kSignedDataRoamingEnabled] =
       settings_private::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[::ash::prefs::kUserBluetoothAdapterEnabled] =
+      settings_private::PrefType::PREF_TYPE_BOOLEAN;
 
   // Timezone settings.
   (*s_whitelist)[chromeos::kSystemTimezone] =
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index 1e12aacd..4933e36 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -66,18 +66,12 @@
 
  protected:
   bool IsTooIntensiveForThisPlatform() const {
-    // The tests are too slow to succeed with software GL on the bots.
-    if (UsingSoftwareGL())
-      return true;
-
 #if defined(NDEBUG)
-    return false;
+    // The tests are too slow to succeed with software GL on the bots.
+    return UsingSoftwareGL();
 #else
-    // TODO(miu): Look into enabling these tests for the Debug build bots once
-    // they prove to be stable again on the Release bots.
-    // http://crbug.com/396413
-    return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-        "run-tab-capture-api-pixel-tests");
+    // The tests only run on release builds.
+    return true;
 #endif
   }
 };
diff --git a/chrome/browser/metrics/android_metrics_provider.cc b/chrome/browser/metrics/android_metrics_provider.cc
index c9f5160..ce3c2a32 100644
--- a/chrome/browser/metrics/android_metrics_provider.cc
+++ b/chrome/browser/metrics/android_metrics_provider.cc
@@ -27,6 +27,11 @@
   return (1 << type);
 }
 
+void EmitLowRamDeviceHistogram() {
+  UMA_HISTOGRAM_BOOLEAN("MemoryAndroid.LowRamDevice",
+                        base::SysInfo::IsLowEndDevice());
+}
+
 }  // namespace
 
 AndroidMetricsProvider::AndroidMetricsProvider(PrefService* local_state)
@@ -40,19 +45,21 @@
 void AndroidMetricsProvider::ProvidePreviousSessionData(
     metrics::ChromeUserMetricsExtension* uma_proto) {
   ConvertStabilityPrefsToHistograms();
+  // The low-ram device status is unlikely to change between browser restarts.
+  // Hence, it's safe and useful to attach this status to a previous session
+  // log.
+  EmitLowRamDeviceHistogram();
 }
 
 void AndroidMetricsProvider::ProvideCurrentSessionData(
     metrics::ChromeUserMetricsExtension* uma_proto) {
   ConvertStabilityPrefsToHistograms();
+  EmitLowRamDeviceHistogram();
   UMA_HISTOGRAM_ENUMERATION(
       "CustomTabs.Visible",
       chrome::android::GetCustomTabsVisibleValue(),
       chrome::android::CUSTOM_TABS_VISIBILITY_MAX);
   UMA_HISTOGRAM_BOOLEAN(
-      "MemoryAndroid.LowRamDevice",
-      base::SysInfo::IsLowEndDevice());
-  UMA_HISTOGRAM_BOOLEAN(
       "Android.MultiWindowMode.Active",
       chrome::android::GetIsInMultiWindowModeValue());
 }
diff --git a/chrome/browser/resources/md_extensions/compiled_resources2.gyp b/chrome/browser/resources/md_extensions/compiled_resources2.gyp
index ab7b556..555897e 100644
--- a/chrome/browser/resources/md_extensions/compiled_resources2.gyp
+++ b/chrome/browser/resources/md_extensions/compiled_resources2.gyp
@@ -185,6 +185,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+        'navigation_helper',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index 355a84ea..8b5d8917 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -67,7 +67,8 @@
         </div>
       </dialog>
       <extensions-view-manager id="viewManager">
-        <extensions-item-list id="items-list" items="[[extensions]]"
+        <extensions-item-list id="items-list"
+            items="[[itemsList_]]"
             delegate="[[delegate]]" in-dev-mode="[[inDevMode]]"
             filter="[[filter]]" hidden$="[[!didInitPage_]]" slot="view">
         </extensions-item-list>
@@ -82,7 +83,8 @@
             data="[[errorPageItem_]]" delegate="[[delegate]]" slot="view">
         </extensions-error-page>
       </extensions-view-manager>
-      <extensions-options-dialog id="options-dialog">
+      <extensions-options-dialog id="options-dialog"
+          on-close="onOptionsDialogClose_">
       </extensions-options-dialog>
       <extensions-pack-dialog id="pack-dialog" delegate="[[delegate]]">
       </extensions-pack-dialog>
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js
index 6f64abe..c6d969e5 100644
--- a/chrome/browser/resources/md_extensions/manager.js
+++ b/chrome/browser/resources/md_extensions/manager.js
@@ -94,6 +94,14 @@
         },
       },
 
+      /** @private {extensions.ShowingType} */
+      listType_: Number,
+
+      itemsList_: {
+        type: Array,
+        computed: 'computeList_(listType_)',
+      },
+
       /**
        * Prevents page content from showing before data is first loaded.
        * @private
@@ -132,19 +140,6 @@
       this.navigationHelper_ = new extensions.NavigationHelper(newPage => {
         this.changePage(newPage, true);
       });
-      this.optionsDialog.addEventListener('close', () => {
-        // We update the page when the options dialog closes, but only if we're
-        // still on the details page. We could be on a different page if the
-        // user hit back while the options dialog was visible; in that case, the
-        // new page is already correct.
-        if (this.currentPage_ && this.currentPage_.page == Page.DETAILS) {
-          // This will update the currentPage_ and the NavigationHelper; since
-          // the active page is already the details page, no main page
-          // transition occurs.
-          this.changePage(
-              {page: Page.DETAILS, extensionId: this.currentPage_.extensionId});
-        }
-      });
     },
 
     get keyboardShortcuts() {
@@ -168,14 +163,6 @@
     },
 
     /**
-     * Shows the details view for a given item.
-     * @param {!chrome.developerPrivate.ExtensionInfo} data
-     */
-    showItemDetails: function(data) {
-      this.changePage({page: Page.DETAILS, extensionId: data.id});
-    },
-
-    /**
      * Initializes the page to reflect what's specified in the url so that if
      * the user visits chrome://extensions/?id=..., we land on the proper page.
      */
@@ -330,6 +317,7 @@
      */
     changePage: function(newPage, isSilent) {
       if (this.currentPage_ && this.currentPage_.page == newPage.page &&
+          this.currentPage_.type == newPage.type &&
           this.currentPage_.subpage == newPage.subpage &&
           this.currentPage_.extensionId == newPage.extensionId) {
         return;
@@ -345,6 +333,9 @@
       if (newPage.extensionId)
         data = assert(this.getData_(newPage.extensionId));
 
+      if (newPage.hasOwnProperty('type'))
+        this.listType_ = newPage.type;
+
       if (toPage == Page.DETAILS)
         this.detailViewItem_ = assert(data);
       else if (toPage == Page.ERRORS)
@@ -353,6 +344,9 @@
       if (fromPage != toPage) {
         /** @type {extensions.ViewManager} */ (this.$.viewManager)
             .switchView(toPage);
+      } else {
+        /** @type {extensions.ViewManager} */ (this.$.viewManager)
+            .animateCurrentView('fade-in');
       }
 
       if (newPage.subpage) {
@@ -373,7 +367,7 @@
      * @private
      */
     onShouldShowItemDetails_: function(e) {
-      this.showItemDetails(e.detail.data);
+      this.changePage({page: Page.DETAILS, extensionId: e.detail.data.id});
     },
 
     /**
@@ -389,19 +383,52 @@
     onDetailsViewClose_: function() {
       // Note: we don't reset detailViewItem_ here because doing so just causes
       // extra work for the data-bound details view.
-      this.changePage({page: Page.LIST});
+      this.changePage({page: Page.LIST, type: this.listType_});
     },
 
     /** @private */
     onErrorPageClose_: function() {
       // Note: we don't reset errorPageItem_ here because doing so just causes
       // extra work for the data-bound error page.
-      this.changePage({page: Page.LIST});
+      this.changePage({page: Page.LIST, type: this.listType_});
     },
 
     /** @private */
     onPackTap_: function() {
       this.$['pack-dialog'].show();
+    },
+
+    /** @private */
+    onOptionsDialogClose_: function() {
+      // We update the page when the options dialog closes, but only if we're
+      // still on the details page. We could be on a different page if the
+      // user hit back while the options dialog was visible; in that case, the
+      // new page is already correct.
+      if (this.currentPage_ && this.currentPage_.page == Page.DETAILS) {
+        // This will update the currentPage_ and the NavigationHelper; since
+        // the active page is already the details page, no main page
+        // transition occurs.
+        this.changePage(
+            {page: Page.DETAILS, extensionId: this.currentPage_.extensionId});
+      }
+    },
+
+    /**
+     * @param {!extensions.ShowingType} listType
+     * @private
+     */
+    computeList_: function(listType) {
+      // TODO(scottchen): the .slice is required to trigger the binding
+      // correctly, otherwise the list won't rerender. Should investigate
+      // the performance implication, or find better ways to trigger change.
+      switch (listType) {
+        case extensions.ShowingType.EXTENSIONS:
+          this.linkPaths('itemsList_', 'extensions');
+          return this.extensions;
+        case extensions.ShowingType.APPS:
+          this.linkPaths('itemsList_', 'apps');
+          return this.apps;
+      }
     }
   });
 
@@ -413,19 +440,9 @@
     }
 
     /** @override */
-    showType(type) {
+    showType(listType) {
       let items;
-      switch (type) {
-        case extensions.ShowingType.EXTENSIONS:
-          items = this.manager_.extensions;
-          break;
-        case extensions.ShowingType.APPS:
-          items = this.manager_.apps;
-          break;
-      }
-
-      this.manager_.$ /* hack */['items-list'].set('items', assert(items));
-      this.manager_.changePage({page: Page.LIST});
+      this.manager_.changePage({page: Page.LIST, type: listType});
     }
 
     /** @override */
diff --git a/chrome/browser/resources/md_extensions/navigation_helper.js b/chrome/browser/resources/md_extensions/navigation_helper.js
index c5272a6..4f058b4 100644
--- a/chrome/browser/resources/md_extensions/navigation_helper.js
+++ b/chrome/browser/resources/md_extensions/navigation_helper.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+cr.exportPath('extensions');
+
 /**
  * The different pages that can be shown at a time.
  * Note: This must remain in sync with the page ids in manager.html!
@@ -19,6 +21,12 @@
   OPTIONS: 'options',
 };
 
+/** @enum {number} */
+extensions.ShowingType = {
+  EXTENSIONS: 0,
+  APPS: 1,
+};
+
 /** @typedef {{page: Page,
                extensionId: (string|undefined),
                subpage: (!Dialog|undefined)}} */
@@ -39,13 +47,9 @@
      *     forward in history; called with the new active page.
      */
     constructor(onHistoryChange) {
-      this.onHistoryChange_ = onHistoryChange;
-      window.addEventListener('popstate', this.onPopState_.bind(this));
-    }
-
-    /** @private */
-    onPopState_() {
-      this.onHistoryChange_(this.getCurrentPage());
+      window.addEventListener('popstate', () => {
+        onHistoryChange(this.getCurrentPage());
+      });
     }
 
     /**
@@ -67,7 +71,10 @@
       if (location.pathname == '/shortcuts')
         return {page: Page.SHORTCUTS};
 
-      return {page: Page.LIST};
+      if (location.pathname == '/apps')
+        return {page: Page.LIST, type: extensions.ShowingType.APPS};
+
+      return {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS};
     }
 
     /**
@@ -78,7 +85,10 @@
       let path;
       switch (entry.page) {
         case Page.LIST:
-          path = '/';
+          if (entry.type && entry.type == extensions.ShowingType.APPS)
+            path = '/apps';
+          else
+            path = '/';
           break;
         case Page.DETAILS:
           if (entry.subpage) {
diff --git a/chrome/browser/resources/md_extensions/sidebar.html b/chrome/browser/resources/md_extensions/sidebar.html
index 635f68e..030c5c1 100644
--- a/chrome/browser/resources/md_extensions/sidebar.html
+++ b/chrome/browser/resources/md_extensions/sidebar.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="navigation_helper.html">
 
 <dom-module id="extensions-sidebar">
   <template>
diff --git a/chrome/browser/resources/md_extensions/sidebar.js b/chrome/browser/resources/md_extensions/sidebar.js
index ee7978e7..cd1cd9c 100644
--- a/chrome/browser/resources/md_extensions/sidebar.js
+++ b/chrome/browser/resources/md_extensions/sidebar.js
@@ -1,16 +1,6 @@
 // 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.
-
-cr.exportPath('extensions');
-
-// Declare this here to make closure compiler happy, and us sad.
-/** @enum {number} */
-extensions.ShowingType = {
-  EXTENSIONS: 0,
-  APPS: 1,
-};
-
 cr.define('extensions', function() {
   /** @interface */
   const SidebarListDelegate = function() {};
diff --git a/chrome/browser/resources/md_extensions/view_manager.js b/chrome/browser/resources/md_extensions/view_manager.js
index 28ee965d..36e5383 100644
--- a/chrome/browser/resources/md_extensions/view_manager.js
+++ b/chrome/browser/resources/md_extensions/view_manager.js
@@ -107,6 +107,19 @@
 
       return Promise.all(promises);
     },
+
+    /**
+     * Helper function to only animate the current view.
+     * @param {string} animation
+     * @return {!Promise}
+     */
+    animateCurrentView: function(animation) {
+      const currentView = assert(this.querySelector('.active'));
+      const animationFunction = extensions.viewAnimations.get(animation);
+      assert(animationFunction);
+
+      return animationFunction(currentView);
+    },
   });
 
   return {viewAnimations: viewAnimations, ViewManager: ViewManager};
diff --git a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
index 790bfe7..df85fd8 100644
--- a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
@@ -48,6 +48,7 @@
  */
 var BrowserChannel = {
   BETA: 'beta-channel',
+  CANARY: 'canary-channel',
   DEV: 'dev-channel',
   STABLE: 'stable-channel',
 };
@@ -102,6 +103,8 @@
     switch (channel) {
       case BrowserChannel.BETA:
         return 'aboutChannelBeta';
+      case BrowserChannel.CANARY:
+        return 'aboutChannelCanary';
       case BrowserChannel.DEV:
         return 'aboutChannelDev';
       case BrowserChannel.STABLE:
@@ -120,6 +123,7 @@
   function isTargetChannelMoreStable(currentChannel, targetChannel) {
     // List of channels in increasing stability order.
     var channelList = [
+      BrowserChannel.CANARY,
       BrowserChannel.DEV,
       BrowserChannel.BETA,
       BrowserChannel.STABLE,
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
index 3a8bee6..45b028b 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
@@ -1,12 +1,15 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../prefs/prefs.html">
+<link rel="import" href="../prefs/prefs_behavior.html">
 <link rel="import" href="../settings_page/settings_animated_pages.html">
 <link rel="import" href="../settings_page/settings_subpage.html">
 <link rel="import" href="../settings_shared_css.html">
@@ -19,34 +22,45 @@
     <settings-animated-pages id="pages" section="bluetooth"
         focus-config="[[focusConfig_]]">
       <neon-animatable route-path="default">
-        <div id="bluetoothDevices"
-            class="settings-box two-line" actionable on-tap="onTap_">
-          <iron-icon icon="[[getIcon_(bluetoothToggleState_)]]"></iron-icon>
-          <div class="middle">
-            $i18n{bluetoothPageTitle}
-            <div class="secondary" id="bluetoothSecondary">
-              [[getOnOffString_(bluetoothToggleState_,
-              '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]]
+        <template is="dom-if" if="[[!isSecondaryUser_]]">
+          <div id="bluetoothDevices"
+              class="settings-box two-line" actionable on-tap="onTap_">
+            <iron-icon icon="[[getIcon_(adapterState_.powered)]]"></iron-icon>
+            <div class="middle">
+              $i18n{bluetoothPageTitle}
+              <div class="secondary" id="bluetoothSecondary">
+                [[getOnOffString_(adapterState_.powered,
+                '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]]
+              </div>
+            </div>
+            <cr-policy-pref-indicator
+                icon-aria-label="$i18n{bluetoothPageTitle}"
+                pref="[[prefs.cros.device.allow_bluetooth]]"
+              hidden="[[prefs.cros.device.allow_bluetooth.value]]">
+            </cr-policy-pref-indicator>
+            <template is="dom-if" if="[[adapterState_.powered]]">
+              <button class="subpage-arrow" is="paper-icon-button-light"
+                  on-tap="onSubpageArrowTap_"
+                  aria-label="$i18n{bluetoothPageTitle}"
+                  aria-describedby="bluetoothSecondary">
+              </button>
+            </template>
+            <div class="separator"></div>
+            <settings-toggle-button id="enableBluetooth"
+                pref="{{prefs.ash.user.bluetooth.adapter_enabled}}"
+                disabled$="[[!adapterState_.available]]" on-tap="stopTap_"
+                label="$i18n{bluetoothToggleA11yLabel}">
+            </settings-toggle-button>
+          </div>
+        </template>
+        <template is="dom-if" if="[[isSecondaryUser_]]">
+          <div id="bluetoothDevices" class="settings-box two-line">
+            <iron-icon class="policy" icon="cr:group"></iron-icon>
+            <div class="middle">
+              [[i18n('networkPrimaryUserControlled', primaryUserEmail_)]]
             </div>
           </div>
-          <cr-policy-pref-indicator icon-aria-label="$i18n{bluetoothPageTitle}"
-              pref="[[prefs.cros.device.allow_bluetooth]]"
-            hidden="[[prefs.cros.device.allow_bluetooth.value]]">
-          </cr-policy-pref-indicator>
-          <template is="dom-if" if="[[bluetoothToggleState_]]">
-            <button class="subpage-arrow" is="paper-icon-button-light"
-                on-tap="onSubpageArrowTap_"
-                aria-label="$i18n{bluetoothPageTitle}"
-                aria-describedby="bluetoothSecondary">
-            </button>
-          </template>
-          <div class="separator"></div>
-          <paper-toggle-button id="enableBluetooth"
-              checked="{{bluetoothToggleState_}}"
-              disabled$="[[bluetoothToggleDisabled_]]" on-tap="stopTap_"
-              aria-label="$i18n{bluetoothToggleA11yLabel}">
-          </paper-toggle-button>
-        </div>
+        </template>
       </neon-animatable>
 
       <template is="dom-if" route-path="/bluetoothDevices">
@@ -54,10 +68,9 @@
             page-title="$i18n{bluetoothPageTitle}">
           <settings-bluetooth-subpage
               adapter-state="[[adapterState_]]"
-              bluetooth-toggle-state="{{bluetoothToggleState_}}"
-              bluetooth-toggle-disabled="[[bluetoothToggleDisabled_]]"
               bluetooth="[[bluetooth]]"
-              bluetooth-private="[[bluetoothPrivate]]">
+              bluetooth-private="[[bluetoothPrivate]]"
+              prefs="{{prefs}}">
           </settings-bluetooth-subpage>
         </settings-subpage>
       </template>
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
index 35b5c439..13d380f 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
@@ -25,6 +25,8 @@
 Polymer({
   is: 'settings-bluetooth-page',
 
+  behaviors: [I18nBehavior, PrefsBehavior],
+
   properties: {
     /** Preferences state. */
     prefs: {
@@ -33,29 +35,6 @@
     },
 
     /**
-     * Reflects the current state of the toggle buttons (in this page and the
-     * subpage). This will be set when the adapter state change or when the user
-     * changes the toggle.
-     * @private
-     */
-    bluetoothToggleState_: {
-      type: Boolean,
-      observer: 'bluetoothToggleStateChanged_',
-    },
-
-    /**
-     * Set to true before the adapter state is received, when the adapter is
-     * unavailable, and while an adapter state change is requested. This
-     * prevents user changes while a change is in progress or when the adapter
-     * is not available.
-     * @private
-     */
-    bluetoothToggleDisabled_: {
-      type: Boolean,
-      value: true,
-    },
-
-    /**
      * The cached bluetooth adapter state.
      * @type {!chrome.bluetooth.AdapterState|undefined}
      * @private
@@ -98,6 +77,30 @@
       type: Object,
       value: chrome.bluetoothPrivate,
     },
+
+    /**
+     * Whether the user is a secondary user.
+     * @private
+     */
+    isSecondaryUser_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('isSecondaryUser');
+      },
+      readOnly: true,
+    },
+
+    /**
+     * Email address for the primary user.
+     * @private
+     */
+    primaryUserEmail_: {
+      type: String,
+      value: function() {
+        return loadTimeData.getString('primaryUserEmail');
+      },
+      readOnly: true,
+    },
   },
 
   observers: ['deviceListChanged_(deviceList_.*)'],
@@ -140,8 +143,8 @@
    * @return {string}
    * @private
    */
-  getIcon_: function() {
-    if (!this.bluetoothToggleState_)
+  getIcon_: function(enabled) {
+    if (!enabled)
       return 'settings:bluetooth-disabled';
     return 'settings:bluetooth';
   },
@@ -164,16 +167,14 @@
    */
   onBluetoothAdapterStateChanged_: function(state) {
     this.adapterState_ = state;
-    this.bluetoothToggleState_ = state.powered;
-    this.bluetoothToggleDisabled_ = !state.available;
   },
 
   /** @private */
   onTap_: function() {
     if (this.adapterState_.available === false)
       return;
-    if (!this.bluetoothToggleState_)
-      this.bluetoothToggleState_ = true;
+    if (!this.adapterState_.powered)
+      this.setPrefValue('ash.user.bluetooth.adapter_enabled', true);
     else
       this.openSubpage_();
   },
@@ -196,23 +197,6 @@
   },
 
   /** @private */
-  bluetoothToggleStateChanged_: function() {
-    if (!this.adapterState_ || this.bluetoothToggleDisabled_ ||
-        this.bluetoothToggleState_ == this.adapterState_.powered) {
-      return;
-    }
-    this.bluetoothToggleDisabled_ = true;
-    this.bluetoothPrivate.setAdapterState(
-        {powered: this.bluetoothToggleState_}, function() {
-          if (chrome.runtime.lastError) {
-            console.error(
-                'Error enabling bluetooth: ' +
-                chrome.runtime.lastError.message);
-          }
-        });
-  },
-
-  /** @private */
   openSubpage_: function() {
     settings.navigateTo(settings.routes.BLUETOOTH_DEVICES);
   }
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
index 7a782ee..c2ab103 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
@@ -6,6 +6,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../settings_shared_css.html">
@@ -40,28 +41,27 @@
     </style>
 
     <div class="settings-box first">
-      <div id="onOff" class="start" on$="[[bluetoothToggleState]]">
-        [[getOnOffString_(bluetoothToggleState,
+      <div id="onOff" class="start" on$="[[adapterState.powered]]">
+        [[getOnOffString_(adapterState.powered,
           '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]]
       </div>
-      <paper-toggle-button id="enableBluetooth"
-          checked="{{bluetoothToggleState}}"
-          disabled$="[[bluetoothToggleDisabled]]"
-          aria-label="$i18n{bluetoothToggleA11yLabel}">
-      </paper-toggle-button>
+      <settings-toggle-button id="enableBluetooth"
+          pref="{{prefs.ash.user.bluetooth.adapter_enabled}}"
+          label="$i18n{bluetoothToggleA11yLabel}">
+      </settings-toggle-button>
     </div>
 
     <!-- Paired device list -->
-    <div class="settings-box first header" hidden="[[!bluetoothToggleState]]">
+    <div class="settings-box first header" hidden="[[!adapterState.powered]]">
       <div class="start">$i18n{bluetoothDeviceListPaired}</div>
     </div>
     <div id="noPairedDevices" class="list-frame"
-        hidden="[[!showNoDevices_(bluetoothToggleState, pairedDeviceList_)]]">
+        hidden="[[!showNoDevices_(adapterState.powered, pairedDeviceList_)]]">
       $i18n{bluetoothNoDevices}
     </div>
     <div id="pairedContainer" class="container"
         scrollable on-device-event="onDeviceEvent_"
-        hidden="[[!showDevices_(bluetoothToggleState, pairedDeviceList_)]]">
+        hidden="[[!showDevices_(adapterState.powered, pairedDeviceList_)]]">
       <iron-list id="pairedDevices" class="vertical-list" preserve-focus
           items="[[pairedDeviceList_]]"
           selection-enabled selected-item="{{selectedPairedItem_}}"
@@ -75,18 +75,18 @@
     </div>
 
     <!-- Unpaired device list -->
-    <div class="settings-box first header" hidden="[[!bluetoothToggleState]]">
+    <div class="settings-box first header" hidden="[[!adapterState.powered]]">
       <div class="start">$i18n{bluetoothDeviceListUnpaired}</div>
       <paper-spinner active="[[showSpinner_]]">
       </paper-spinner>
     </div>
     <div id="noUnpairedDevices" class="list-frame"
-        hidden="[[!showNoDevices_(bluetoothToggleState, unpairedDeviceList_)]]">
+        hidden="[[!showNoDevices_(adapterState.powered, unpairedDeviceList_)]]">
       $i18n{bluetoothNoDevicesFound}
     </div>
     <div id="unpairedContainer" class="container"
         scrollable on-device-event="onDeviceEvent_"
-        hidden="[[!showDevices_(bluetoothToggleState, unpairedDeviceList_)]]">
+        hidden="[[!showDevices_(adapterState.powered, unpairedDeviceList_)]]">
       <iron-list id="unpairedDevices" class="vertical-list" preserve-focus
           items="[[unpairedDeviceList_]]"
           selection-enabled selected-item="{{selectedUnpairedItem_}}"
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js
index 5487662..3bae2526 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js
@@ -18,9 +18,9 @@
   ],
 
   properties: {
-    /** Reflects the bluetooth-page property. */
-    bluetoothToggleState: {
-      type: Boolean,
+    /** Preferences state. */
+    prefs: {
+      type: Object,
       notify: true,
     },
 
@@ -239,7 +239,7 @@
    * @private
    */
   updateDeviceList_: function() {
-    if (!this.bluetoothToggleState) {
+    if (!this.adapterState.powered) {
       this.deviceList_ = [];
       return;
     }
@@ -342,23 +342,23 @@
   },
 
   /**
-   * @param {boolean} bluetoothToggleState
+   * @param {boolean} adapterPoweredState
    * @param {!Array<!chrome.bluetooth.Device>} deviceList
    * @return {boolean}
    * @private
    */
-  showDevices_: function(bluetoothToggleState, deviceList) {
-    return bluetoothToggleState && deviceList.length > 0;
+  showDevices_: function(adapterPoweredState, deviceList) {
+    return adapterPoweredState && deviceList.length > 0;
   },
 
   /**
-   * @param {boolean} bluetoothToggleState
+   * @param {boolean} adapterPoweredState
    * @param {!Array<!chrome.bluetooth.Device>} deviceList
    * @return {boolean}
    * @private
    */
-  showNoDevices_: function(bluetoothToggleState, deviceList) {
-    return bluetoothToggleState && deviceList.length == 0;
+  showNoDevices_: function(adapterPoweredState, deviceList) {
+    return adapterPoweredState && deviceList.length == 0;
   },
 
   /**
diff --git a/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp b/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp
index 3d4051b..d7d70897 100644
--- a/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp
@@ -7,6 +7,7 @@
       'target_name': 'bluetooth_page',
       'dependencies': [
         '../compiled_resources2.gyp:route',
+        '../prefs/compiled_resources2.gyp:prefs_behavior',
         '../settings_page/compiled_resources2.gyp:settings_animated_pages',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
@@ -21,6 +22,7 @@
       'target_name': 'bluetooth_subpage',
       'dependencies': [
         '../compiled_resources2.gyp:route',
+        '../prefs/compiled_resources2.gyp:prefs_behavior',
         '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_scrollable_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
diff --git a/chrome/browser/resources/settings/internet_page/network_siminfo.js b/chrome/browser/resources/settings/internet_page/network_siminfo.js
index e9e19cfe..cb0eb9c 100644
--- a/chrome/browser/resources/settings/internet_page/network_siminfo.js
+++ b/chrome/browser/resources/settings/internet_page/network_siminfo.js
@@ -78,7 +78,7 @@
     var simLockStatus = this.networkProperties.Cellular.SIMLockStatus;
     this.pukRequired_ =
         !!simLockStatus && simLockStatus.LockType == CrOnc.LockType.PUK;
-    this.lockEnabled_ = simLockStatus.LockEnabled;
+    this.lockEnabled_ = !!simLockStatus && simLockStatus.LockEnabled;
   },
 
   /** @private */
@@ -164,7 +164,7 @@
   onChangePinTap_: function(event) {
     if (!this.networkProperties || !this.networkProperties.Cellular)
       return;
-    event.preventDefault();
+    event.stopPropagation();
     this.error_ = ErrorType.NONE;
     this.$.changePinOld.value = '';
     this.$.changePinNew1.value = '';
@@ -205,7 +205,7 @@
    * @private
    */
   onUnlockPinTap_: function(event) {
-    event.preventDefault();
+    event.stopPropagation();
     if (this.pukRequired_) {
       this.showUnlockPukDialog_();
     } else {
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 0ed7469f..656d666 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -138,9 +138,12 @@
         this.deviceIsEnabled_(deviceState)) {
       return false;
     }
-    return deviceState.SimPresent === false ||
-        deviceState.SimLockType == CrOnc.LockType.PIN ||
-        deviceState.SimLockType == CrOnc.LockType.PUK;
+    if (deviceState.SIMPresent === false)
+      return true;
+    var simLockType =
+        deviceState.SIMLockStatus ? deviceState.SIMLockStatus.LockType : '';
+    return simLockType == CrOnc.LockType.PIN ||
+        simLockType == CrOnc.LockType.PUK;
   },
 
   /**
@@ -157,11 +160,8 @@
       GUID: '',
       Type: CrOnc.Type.CELLULAR,
       Cellular: {
-        SIMLockStatus: {
-          LockType: deviceState.SimLockType || '',
-          LockEnabled: deviceState.SimLockType != CrOnc.LockType.NONE,
-        },
-        SIMPresent: deviceState.SimPresent,
+        SIMLockStatus: deviceState.SIMLockStatus,
+        SIMPresent: deviceState.SIMPresent,
       },
     };
   },
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 200bcfd..6ca6601 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
 #include "base/task_scheduler/post_task.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h"
@@ -798,6 +799,7 @@
   request.set_length(item_->GetReceivedBytes());
   request.set_skipped_url_whitelist(skipped_url_whitelist_);
   request.set_skipped_certificate_whitelist(skipped_certificate_whitelist_);
+  request.set_locale(g_browser_process->GetApplicationLocale());
   for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
     ClientDownloadRequest::Resource* resource = request.add_resources();
     resource->set_url(SanitizeUrl(item_->GetUrlChain()[i]));
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index 0520778d..5bef7eb 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -82,12 +82,15 @@
     return nullptr;
 
   ash::mojom::UserSessionPtr session = ash::mojom::UserSession::New();
+  Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(&user);
   session->session_id = user_session_id;
   session->user_info = ash::mojom::UserInfo::New();
   session->user_info->type = user.GetType();
   session->user_info->account_id = user.GetAccountId();
   session->user_info->display_name = base::UTF16ToUTF8(user.display_name());
   session->user_info->display_email = user.display_email();
+  if (profile)
+    session->user_info->is_new_profile = profile->IsNewProfile();
 
   session->user_info->avatar = user.GetImage();
   if (session->user_info->avatar.isNull()) {
@@ -97,7 +100,6 @@
   }
 
   if (user.IsSupervised()) {
-    Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(&user);
     if (profile) {
       SupervisedUserService* service =
           SupervisedUserServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 891a1c0..5f5d01c 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -391,7 +391,8 @@
   // HistoryQuery. Please update it whenever you add or remove any keys in
   // results_info_value_.
   results_info.SetString("term", query_results_info.search_text);
-  results_info.SetBoolean("finished", query_results_info.reached_beginning);
+  results_info.SetBoolean("finished",
+                          query_results_info.reached_beginning_of_local);
   results_info.SetBoolean("hasSyncedResults",
                           query_results_info.has_synced_results);
 
diff --git a/chrome/browser/ui/webui/help/help_handler.cc b/chrome/browser/ui/webui/help/help_handler.cc
deleted file mode 100644
index 6aad55c9..0000000
--- a/chrome/browser/ui/webui/help/help_handler.cc
+++ /dev/null
@@ -1,766 +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/help/help_handler.h"
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/base_switches.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/i18n/message_formatter.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/obsolete_system/obsolete_system.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/upgrade_detector.h"
-#include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_content_client.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/google/core/browser/google_util.h"
-#include "components/policy/core/common/policy_namespace.h"
-#include "components/policy/policy_constants.h"
-#include "components/strings/grit/components_chromium_strings.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/version_info/version_info.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "v8/include/v8-version-string.h"
-
-#if defined(OS_CHROMEOS)
-#include "base/files/file_util_proxy.h"
-#include "base/i18n/time_formatting.h"
-#include "base/sys_info.h"
-#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
-#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
-#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chromeos/image_source.h"
-#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
-#include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
-#include "chromeos/chromeos_switches.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/power_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
-#include "components/prefs/pref_service.h"
-#include "components/user_manager/user_manager.h"
-#include "ui/chromeos/devicetype_utils.h"
-#endif
-
-using base::ListValue;
-using content::BrowserThread;
-
-namespace {
-
-#if defined(OS_CHROMEOS)
-
-// Directory containing the regulatory labels for supported regions.
-const char kRegulatoryLabelsDirectory[] = "regulatory_labels";
-
-// File names of the image file and the file containing alt text for the label.
-const char kRegulatoryLabelImageFilename[] = "label.png";
-const char kRegulatoryLabelTextFilename[] = "label.txt";
-
-// Default region code to use if there's no label for the VPD region code.
-const char kDefaultRegionCode[] = "us";
-
-struct RegulatoryLabel {
-  const std::string label_text;
-  const std::string image_url;
-};
-
-// Returns message that informs user that for update it's better to
-// connect to a network of one of the allowed types.
-base::string16 GetAllowedConnectionTypesMessage() {
-  // Old help page does not support interactive-updates over cellular, so just
-  // sets |interactive| to false to make its behavior the same as before.
-  if (help_utils_chromeos::IsUpdateOverCellularAllowed(
-          false /* interactive */)) {
-    return l10n_util::GetStringUTF16(IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
-  } else {
-    return l10n_util::GetStringUTF16(
-        IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED);
-  }
-}
-
-// Returns true if the device is enterprise managed, false otherwise.
-bool IsEnterpriseManaged() {
-  policy::BrowserPolicyConnectorChromeOS* connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
-  return connector->IsEnterpriseManaged();
-}
-
-// Returns true if current user can change channel, false otherwise.
-bool CanChangeChannel(Profile* profile) {
-  bool value = false;
-  chromeos::CrosSettings::Get()->GetBoolean(chromeos::kReleaseChannelDelegated,
-                                            &value);
-
-  // On a managed machine we delegate this setting to the users of the same
-  // domain only if the policy value is "domain".
-  if (IsEnterpriseManaged()) {
-    if (!value)
-      return false;
-    // Get the currently logged in user and strip the domain part only.
-    std::string domain = "";
-    const user_manager::User* user =
-        profile ? chromeos::ProfileHelper::Get()->GetUserByProfile(profile)
-                : nullptr;
-    std::string email =
-        user ? user->GetAccountId().GetUserEmail() : std::string();
-    size_t at_pos = email.find('@');
-    if (at_pos != std::string::npos && at_pos + 1 < email.length())
-      domain = email.substr(email.find('@') + 1);
-    policy::BrowserPolicyConnectorChromeOS* connector =
-        g_browser_process->platform_part()->browser_policy_connector_chromeos();
-    return domain == connector->GetEnterpriseEnrollmentDomain();
-  } else {
-    chromeos::OwnerSettingsServiceChromeOS* service =
-        chromeos::OwnerSettingsServiceChromeOSFactory::GetInstance()
-            ->GetForBrowserContext(profile);
-    // On non managed machines we have local owner who is the only one to change
-    // anything. Ensure that ReleaseChannelDelegated is false.
-    if (service && service->IsOwner())
-      return !value;
-  }
-  return false;
-}
-
-// Returns the path of the regulatory labels directory for a given region, if
-// found. Must be called from the blocking pool.
-base::FilePath GetRegulatoryLabelDirForRegion(const std::string& region) {
-  // Generate the path under the asset dir or URL host to the regulatory files
-  // for the region, e.g., "regulatory_labels/us/".
-  const base::FilePath region_path =
-      base::FilePath(kRegulatoryLabelsDirectory).AppendASCII(region);
-
-  // Check for file existence starting in /usr/share/chromeos-assets/, e.g.,
-  // "/usr/share/chromeos-assets/regulatory_labels/us/label.png".
-  const base::FilePath asset_dir(chrome::kChromeOSAssetPath);
-  if (base::PathExists(asset_dir.Append(region_path)
-                           .AppendASCII(kRegulatoryLabelImageFilename))) {
-    return region_path;
-  }
-
-  return base::FilePath();
-}
-
-// Finds the directory for the regulatory label, using the VPD region code.
-// Also tries "us" as a fallback region. Must be called from the blocking pool.
-base::FilePath FindRegulatoryLabelDir() {
-  std::string region;
-  base::FilePath region_path;
-  // Use the VPD region code to find the label dir.
-  if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
-          "region", &region) && !region.empty()) {
-    region_path = GetRegulatoryLabelDirForRegion(region);
-  }
-
-  // Try the fallback region code if no directory was found.
-  if (region_path.empty() && region != kDefaultRegionCode)
-    region_path = GetRegulatoryLabelDirForRegion(kDefaultRegionCode);
-
-  return region_path;
-}
-
-// Reads the file containing the regulatory label text, if found, relative to
-// the asset directory. Must be called from the blocking pool.
-std::string ReadRegulatoryLabelText(const base::FilePath& path) {
-  base::FilePath text_path(chrome::kChromeOSAssetPath);
-  text_path = text_path.Append(path);
-  text_path = text_path.AppendASCII(kRegulatoryLabelTextFilename);
-
-  std::string contents;
-  if (base::ReadFileToString(text_path, &contents))
-    return contents;
-  return std::string();
-}
-#endif  // defined(OS_CHROMEOS)
-
-}  // namespace
-
-HelpHandler::HelpHandler()
-    : policy_registrar_(
-          g_browser_process->policy_service(),
-          policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())),
-      apply_changes_from_upgrade_observer_(false),
-      weak_factory_(this) {
-  UpgradeDetector::GetInstance()->AddObserver(this);
-}
-
-HelpHandler::~HelpHandler() {
-  UpgradeDetector::GetInstance()->RemoveObserver(this);
-}
-
-void HelpHandler::GetLocalizedValues(base::DictionaryValue* localized_strings) {
-  struct L10nResources {
-    const char* name;
-    int ids;
-  };
-
-  static L10nResources resources[] = {
-    {"aboutTitle", IDS_ABOUT_TITLE},
-#if defined(OS_CHROMEOS)
-    {"aboutProductTitle", IDS_PRODUCT_OS_NAME},
-#else
-    {"aboutProductTitle", IDS_PRODUCT_NAME},
-#endif
-    {"aboutProductDescription", IDS_ABOUT_PRODUCT_DESCRIPTION},
-    {"relaunch", IDS_RELAUNCH_BUTTON},
-#if defined(OS_CHROMEOS)
-    {"relaunchAndPowerwash", IDS_RELAUNCH_AND_POWERWASH_BUTTON},
-#endif
-    {"productName", IDS_PRODUCT_NAME},
-    {"updateCheckStarted", IDS_UPGRADE_CHECK_STARTED},
-    {"updating", IDS_UPGRADE_UPDATING},
-#if defined(OS_CHROMEOS) || defined(OS_WIN)
-    {"updateDisabledByPolicy", IDS_UPGRADE_DISABLED_BY_POLICY},
-#endif  // defined(OS_CHROMEOS) || defined(OS_WIN)
-#if defined(OS_CHROMEOS)
-    {"updateButton", IDS_UPGRADE_BUTTON},
-    {"updatingChannelSwitch", IDS_UPGRADE_UPDATING_CHANNEL_SWITCH},
-#endif
-    {"updateAlmostDone", IDS_UPGRADE_SUCCESSFUL_RELAUNCH},
-#if defined(OS_CHROMEOS)
-    {"successfulChannelSwitch", IDS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH},
-#endif
-    {"getHelpWithChrome", IDS_GET_HELP_USING_CHROME},
-    {"reportAnIssue", IDS_REPORT_AN_ISSUE},
-#if defined(OS_CHROMEOS)
-    {"platform", IDS_PLATFORM_LABEL},
-    {"arcVersion", IDS_ARC_VERSION_LABEL},
-    {"firmware", IDS_ABOUT_PAGE_FIRMWARE},
-    {"showMoreInfo", IDS_SHOW_MORE_INFO},
-    {"hideMoreInfo", IDS_HIDE_MORE_INFO},
-    {"channel", IDS_ABOUT_PAGE_CHANNEL},
-    {"stable", IDS_ABOUT_PAGE_CHANNEL_STABLE},
-    {"beta", IDS_ABOUT_PAGE_CHANNEL_BETA},
-    {"dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT},
-    {"devChannelDisclaimer", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT_DISCLAIMER},
-    {"channel-changed", IDS_ABOUT_PAGE_CHANNEL_CHANGED},
-    {"currentChannelStable", IDS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE},
-    {"currentChannelBeta", IDS_ABOUT_PAGE_CURRENT_CHANNEL_BETA},
-    {"currentChannelDev", IDS_ABOUT_PAGE_CURRENT_CHANNEL_DEV},
-    {"currentChannel", IDS_ABOUT_PAGE_CURRENT_CHANNEL},
-    {"channelChangeButton", IDS_ABOUT_PAGE_CHANNEL_CHANGE_BUTTON},
-    {"channelChangeDisallowedMessage",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_DISALLOWED_MESSAGE},
-    {"channelChangePageTitle", IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_TITLE},
-    {"channelChangePagePowerwashTitle",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_TITLE},
-    {"channelChangePagePowerwashMessage",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_MESSAGE},
-    {"channelChangePageDelayedChangeTitle",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_TITLE},
-    {"channelChangePageUnstableTitle",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_TITLE},
-    {"channelChangePagePowerwashButton",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_BUTTON},
-    {"channelChangePageChangeButton",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CHANGE_BUTTON},
-    {"channelChangePageCancelButton",
-     IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CANCEL_BUTTON},
-    {"userAgent", IDS_VERSION_UI_USER_AGENT},
-    {"commandLine", IDS_VERSION_UI_COMMAND_LINE},
-    {"buildDate", IDS_VERSION_UI_BUILD_DATE},
-#endif
-#if defined(OS_MACOSX) || defined(OS_WIN)
-    {"learnMore", IDS_LEARN_MORE},
-#endif
-#if defined(OS_MACOSX)
-    {"promote", IDS_ABOUT_CHROME_PROMOTE_UPDATER},
-#endif
-  };
-
-  for (size_t i = 0; i < arraysize(resources); ++i) {
-    localized_strings->SetString(resources[i].name,
-                                 l10n_util::GetStringUTF16(resources[i].ids));
-  }
-
-#if defined(OS_CHROMEOS)
-  localized_strings->SetString(
-      "upToDate", ui::SubstituteChromeOSDeviceType(IDS_UPGRADE_UP_TO_DATE));
-#else
-  localized_strings->SetString("upToDate", l10n_util::GetStringUTF16(
-      IDS_UPGRADE_UP_TO_DATE));
-#endif
-
-  localized_strings->SetString("updateObsoleteSystem",
-                               ObsoleteSystem::LocalizedObsoleteString());
-  localized_strings->SetString("updateObsoleteSystemURL",
-                               ObsoleteSystem::GetLinkURL());
-
-  localized_strings->SetString(
-      "browserVersion",
-      l10n_util::GetStringFUTF16(IDS_ABOUT_PRODUCT_VERSION,
-                                 BuildBrowserVersionString()));
-
-  localized_strings->SetString(
-      "productCopyright",
-      base::i18n::MessageFormatter::FormatWithNumberedArgs(
-          l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
-          base::Time::Now()));
-
-  base::string16 license = l10n_util::GetStringFUTF16(
-      IDS_VERSION_UI_LICENSE, base::ASCIIToUTF16(chrome::kChromiumProjectURL),
-      base::ASCIIToUTF16(chrome::kChromeUICreditsURL));
-  localized_strings->SetString("productLicense", license);
-
-#if defined(OS_CHROMEOS)
-  base::string16 os_license = l10n_util::GetStringFUTF16(
-      IDS_ABOUT_CROS_VERSION_LICENSE,
-      base::ASCIIToUTF16(chrome::kChromeUIOSCreditsURL));
-  localized_strings->SetString("productOsLicense", os_license);
-
-  base::string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME);
-  localized_strings->SetString(
-      "channelChangePageDelayedChangeMessage",
-      l10n_util::GetStringFUTF16(
-          IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_MESSAGE,
-          product_name));
-  localized_strings->SetString(
-      "channelChangePageUnstableMessage",
-      l10n_util::GetStringFUTF16(
-          IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_MESSAGE,
-          product_name));
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableNewChannelSwitcherUI)) {
-    localized_strings->SetBoolean("disableNewChannelSwitcherUI", true);
-  }
-
-  localized_strings->SetString(
-      "eolLearnMore", l10n_util::GetStringFUTF16(
-                          IDS_ABOUT_PAGE_EOL_LEARN_MORE,
-                          base::ASCIIToUTF16(chrome::kEolNotificationURL)));
-#endif
-
-  base::string16 tos = l10n_util::GetStringFUTF16(
-      IDS_ABOUT_TERMS_OF_SERVICE, base::UTF8ToUTF16(chrome::kChromeUITermsURL));
-  localized_strings->SetString("productTOS", tos);
-
-  localized_strings->SetString("jsEngine", "V8");
-  localized_strings->SetString("jsEngineVersion", V8_VERSION_STRING);
-
-  localized_strings->SetString("userAgentInfo", GetUserAgent());
-
-  base::CommandLine::StringType command_line =
-      base::CommandLine::ForCurrentProcess()->GetCommandLineString();
-  localized_strings->SetString("commandLineInfo", command_line);
-}
-
-void HelpHandler::RegisterMessages() {
-  version_updater_.reset(VersionUpdater::Create(web_ui()->GetWebContents()));
-  apply_changes_from_upgrade_observer_ = true;
-  policy_registrar_.Observe(
-      policy::key::kDeviceAutoUpdateDisabled,
-      base::Bind(&HelpHandler::OnDeviceAutoUpdatePolicyChanged,
-                 base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback("onPageLoaded",
-      base::Bind(&HelpHandler::OnPageLoaded, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("relaunchNow",
-      base::Bind(&HelpHandler::RelaunchNow, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("openFeedbackDialog",
-      base::Bind(&HelpHandler::OpenFeedbackDialog, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("openHelpPage",
-      base::Bind(&HelpHandler::OpenHelpPage, base::Unretained(this)));
-#if defined(OS_CHROMEOS)
-  web_ui()->RegisterMessageCallback("setChannel",
-      base::Bind(&HelpHandler::SetChannel, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("relaunchAndPowerwash",
-      base::Bind(&HelpHandler::RelaunchAndPowerwash, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("requestUpdate",
-      base::Bind(&HelpHandler::RequestUpdate, base::Unretained(this)));
-#endif
-#if defined(OS_MACOSX)
-  web_ui()->RegisterMessageCallback("promoteUpdater",
-      base::Bind(&HelpHandler::PromoteUpdater, base::Unretained(this)));
-#endif
-
-#if defined(OS_CHROMEOS)
-  // Handler for the product label image, which will be shown if available.
-  content::URLDataSource::Add(Profile::FromWebUI(web_ui()),
-                              new chromeos::ImageSource());
-#endif
-}
-
-void HelpHandler::OnUpgradeRecommended() {
-  if (apply_changes_from_upgrade_observer_) {
-    // A version update is installed and ready to go. Refresh the UI so the
-    // correct state will be shown.
-    RequestUpdate(nullptr);
-  }
-}
-
-// static
-base::string16 HelpHandler::BuildBrowserVersionString() {
-  std::string version = version_info::GetVersionNumber();
-
-  std::string modifier = chrome::GetChannelString();
-  if (!modifier.empty())
-    version += " " + modifier;
-
-#if defined(ARCH_CPU_64_BITS)
-  version += " (64-bit)";
-#endif
-
-  return base::UTF8ToUTF16(version);
-}
-
-void HelpHandler::OnDeviceAutoUpdatePolicyChanged(
-    const base::Value* previous_policy,
-    const base::Value* current_policy) {
-  bool previous_auto_update_disabled = false;
-  if (previous_policy)
-    CHECK(previous_policy->GetAsBoolean(&previous_auto_update_disabled));
-
-  bool current_auto_update_disabled = false;
-  if (current_policy)
-    CHECK(current_policy->GetAsBoolean(&current_auto_update_disabled));
-
-  if (current_auto_update_disabled != previous_auto_update_disabled) {
-    // Refresh the update status to refresh the status of the UI.
-    RefreshUpdateStatus();
-  }
-}
-
-void HelpHandler::RefreshUpdateStatus() {
-  // On Chrome OS, do not check for an update automatically.
-#if defined(OS_CHROMEOS)
-  static_cast<VersionUpdaterCros*>(version_updater_.get())->GetUpdateStatus(
-      base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)));
-#else
-  RequestUpdate(NULL);
-#endif
-}
-
-void HelpHandler::OnPageLoaded(const base::ListValue* args) {
-#if defined(OS_CHROMEOS)
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(&chromeos::version_loader::GetVersion,
-                 chromeos::version_loader::VERSION_FULL),
-      base::Bind(&HelpHandler::OnOSVersion, weak_factory_.GetWeakPtr()));
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(&chromeos::version_loader::GetARCVersion),
-      base::Bind(&HelpHandler::OnARCVersion, weak_factory_.GetWeakPtr()));
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(&chromeos::version_loader::GetFirmware),
-      base::Bind(&HelpHandler::OnOSFirmware, weak_factory_.GetWeakPtr()));
-
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "help.HelpPage.updateEnableReleaseChannel",
-      base::Value(CanChangeChannel(Profile::FromWebUI(web_ui()))));
-
-  base::Time build_time = base::SysInfo::GetLsbReleaseTime();
-  base::string16 build_date = base::TimeFormatFriendlyDate(build_time);
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setBuildDate",
-                                         base::Value(build_date));
-#endif  // defined(OS_CHROMEOS)
-
-  RefreshUpdateStatus();
-
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "help.HelpPage.setObsoleteSystem",
-      base::Value(ObsoleteSystem::IsObsoleteNowOrSoon()));
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "help.HelpPage.setObsoleteSystemEndOfTheLine",
-      base::Value(ObsoleteSystem::IsObsoleteNowOrSoon() &&
-                  ObsoleteSystem::IsEndOfTheLine()));
-
-#if defined(OS_CHROMEOS)
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "help.HelpPage.updateIsEnterpriseManaged",
-      base::Value(IsEnterpriseManaged()));
-  // First argument to GetChannel() is a flag that indicates whether
-  // current channel should be returned (if true) or target channel
-  // (otherwise).
-  version_updater_->GetChannel(true,
-      base::Bind(&HelpHandler::OnCurrentChannel, weak_factory_.GetWeakPtr()));
-  version_updater_->GetChannel(false,
-      base::Bind(&HelpHandler::OnTargetChannel, weak_factory_.GetWeakPtr()));
-
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableEolNotification)) {
-    version_updater_->GetEolStatus(
-        base::Bind(&HelpHandler::OnEolStatus, weak_factory_.GetWeakPtr()));
-  }
-
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(&FindRegulatoryLabelDir),
-      base::Bind(&HelpHandler::OnRegulatoryLabelDirFound,
-                 weak_factory_.GetWeakPtr()));
-#endif
-}
-
-#if defined(OS_MACOSX)
-void HelpHandler::PromoteUpdater(const base::ListValue* args) {
-  version_updater_->PromoteUpdater();
-}
-#endif
-
-void HelpHandler::RelaunchNow(const base::ListValue* args) {
-  DCHECK(args->empty());
-  chrome::AttemptRelaunch();
-}
-
-void HelpHandler::OpenFeedbackDialog(const base::ListValue* args) {
-  DCHECK(args->empty());
-  Browser* browser = chrome::FindBrowserWithWebContents(
-      web_ui()->GetWebContents());
-  chrome::OpenFeedbackDialog(browser,
-                             chrome::kFeedbackSourceOldSettingsAboutPage);
-}
-
-void HelpHandler::OpenHelpPage(const base::ListValue* args) {
-  DCHECK(args->empty());
-  Browser* browser = chrome::FindBrowserWithWebContents(
-      web_ui()->GetWebContents());
-  chrome::ShowHelp(browser, chrome::HELP_SOURCE_WEBUI);
-}
-
-#if defined(OS_CHROMEOS)
-
-void HelpHandler::SetChannel(const base::ListValue* args) {
-  DCHECK(args->GetSize() == 2);
-
-  if (!CanChangeChannel(Profile::FromWebUI(web_ui()))) {
-    LOG(WARNING) << "Non-owner tried to change release track.";
-    return;
-  }
-
-  base::string16 channel;
-  bool is_powerwash_allowed;
-  if (!args->GetString(0, &channel) ||
-      !args->GetBoolean(1, &is_powerwash_allowed)) {
-    LOG(ERROR) << "Can't parse SetChannel() args";
-    return;
-  }
-
-  version_updater_->SetChannel(base::UTF16ToUTF8(channel),
-                               is_powerwash_allowed);
-  if (user_manager::UserManager::Get()->IsCurrentUserOwner()) {
-    // Check for update after switching release channel.
-    version_updater_->CheckForUpdate(base::Bind(&HelpHandler::SetUpdateStatus,
-                                                base::Unretained(this)),
-                                     VersionUpdater::PromoteCallback());
-  }
-}
-
-void HelpHandler::RelaunchAndPowerwash(const base::ListValue* args) {
-  DCHECK(args->empty());
-
-  if (IsEnterpriseManaged())
-    return;
-
-  PrefService* prefs = g_browser_process->local_state();
-  prefs->SetBoolean(prefs::kFactoryResetRequested, true);
-  prefs->CommitPendingWrite();
-
-  // Perform sign out. Current chrome process will then terminate, new one will
-  // be launched (as if it was a restart).
-  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
-}
-
-#endif  // defined(OS_CHROMEOS)
-
-void HelpHandler::RequestUpdate(const base::ListValue* args) {
-  VersionUpdater::PromoteCallback promote_callback;
-  version_updater_->CheckForUpdate(
-      base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)),
-#if defined(OS_MACOSX)
-      base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this)));
-#else
-      VersionUpdater::PromoteCallback());
-#endif  // OS_MACOSX
-}
-
-void HelpHandler::SetUpdateStatus(VersionUpdater::Status status,
-                                  int progress,
-                                  const std::string& /* version */,
-                                  int64_t /* size */,
-                                  const base::string16& message) {
-  // Only UPDATING state should have progress set.
-  DCHECK(status == VersionUpdater::UPDATING || progress == 0);
-
-  std::string status_str;
-  switch (status) {
-  case VersionUpdater::CHECKING:
-    status_str = "checking";
-    break;
-  case VersionUpdater::UPDATING:
-    status_str = "updating";
-    break;
-  case VersionUpdater::NEARLY_UPDATED:
-    status_str = "nearly_updated";
-    break;
-  case VersionUpdater::UPDATED:
-    status_str = "updated";
-    break;
-  case VersionUpdater::FAILED:
-  case VersionUpdater::FAILED_OFFLINE:
-  case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED:
-  // Old help page does not support update over cellular connection. Treat this
-  // signal as FAILED.
-  case VersionUpdater::NEED_PERMISSION_TO_UPDATE:
-    status_str = "failed";
-    break;
-  case VersionUpdater::DISABLED:
-    status_str = "disabled";
-    break;
-  case VersionUpdater::DISABLED_BY_ADMIN:
-    status_str = "disabled_by_admin";
-    break;
-  }
-
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setUpdateStatus",
-                                         base::Value(status_str),
-                                         base::Value(message));
-
-  if (status == VersionUpdater::UPDATING) {
-    web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setProgress",
-                                           base::Value(progress));
-  }
-
-#if defined(OS_CHROMEOS)
-  if (status == VersionUpdater::FAILED_OFFLINE ||
-      status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) {
-    base::string16 types_msg = GetAllowedConnectionTypesMessage();
-    if (!types_msg.empty()) {
-      web_ui()->CallJavascriptFunctionUnsafe(
-          "help.HelpPage.setAndShowAllowedConnectionTypesMsg",
-          base::Value(types_msg));
-    } else {
-      web_ui()->CallJavascriptFunctionUnsafe(
-          "help.HelpPage.showAllowedConnectionTypesMsg", base::Value(false));
-    }
-  } else {
-    web_ui()->CallJavascriptFunctionUnsafe(
-        "help.HelpPage.showAllowedConnectionTypesMsg", base::Value(false));
-  }
-#endif  // defined(OS_CHROMEOS)
-}
-
-#if defined(OS_MACOSX)
-void HelpHandler::SetPromotionState(VersionUpdater::PromotionState state) {
-  std::string state_str;
-  switch (state) {
-  case VersionUpdater::PROMOTE_HIDDEN:
-  case VersionUpdater::PROMOTED:
-    state_str = "hidden";
-    break;
-  case VersionUpdater::PROMOTE_ENABLED:
-    state_str = "enabled";
-    break;
-  case VersionUpdater::PROMOTE_DISABLED:
-    state_str = "disabled";
-    break;
-  }
-
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setPromotionState",
-                                         base::Value(state_str));
-}
-#endif  // defined(OS_MACOSX)
-
-#if defined(OS_CHROMEOS)
-void HelpHandler::OnOSVersion(const std::string& version) {
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setOSVersion",
-                                         base::Value(version));
-}
-
-void HelpHandler::OnARCVersion(const std::string& firmware) {
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setARCVersion",
-                                         base::Value(firmware));
-}
-
-void HelpHandler::OnOSFirmware(const std::string& firmware) {
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setOSFirmware",
-                                         base::Value(firmware));
-}
-
-void HelpHandler::OnCurrentChannel(const std::string& channel) {
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateCurrentChannel",
-                                         base::Value(channel));
-}
-
-void HelpHandler::OnTargetChannel(const std::string& channel) {
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateTargetChannel",
-                                         base::Value(channel));
-}
-
-void HelpHandler::OnRegulatoryLabelDirFound(const base::FilePath& path) {
-  if (path.empty())
-    return;
-
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(&ReadRegulatoryLabelText, path),
-      base::Bind(&HelpHandler::OnRegulatoryLabelTextRead,
-                 weak_factory_.GetWeakPtr()));
-
-  // Send the image path to the WebUI.
-  OnRegulatoryLabelImageFound(path.AppendASCII(kRegulatoryLabelImageFilename));
-}
-
-void HelpHandler::OnRegulatoryLabelImageFound(const base::FilePath& path) {
-  std::string url = std::string("chrome://") + chrome::kChromeOSAssetHost +
-      "/" + path.MaybeAsASCII();
-  web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setRegulatoryLabelPath",
-                                         base::Value(url));
-}
-
-void HelpHandler::OnRegulatoryLabelTextRead(const std::string& text) {
-  // Remove unnecessary whitespace.
-  web_ui()->CallJavascriptFunctionUnsafe(
-      "help.HelpPage.setRegulatoryLabelText",
-      base::Value(base::CollapseWhitespaceASCII(text, true)));
-}
-
-void HelpHandler::OnEolStatus(update_engine::EndOfLifeStatus status) {
-  // Security only state is no longer supported.
-  if (status == update_engine::EndOfLifeStatus::kSecurityOnly ||
-      IsEnterpriseManaged()) {
-    return;
-  }
-
-  if (status == update_engine::EndOfLifeStatus::kSupported) {
-    web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateEolMessage",
-                                           base::Value("device_supported"),
-                                           base::Value(""));
-  } else {
-    web_ui()->CallJavascriptFunctionUnsafe(
-        "help.HelpPage.updateEolMessage", base::Value("device_endoflife"),
-        base::Value(l10n_util::GetStringUTF16(IDS_ABOUT_PAGE_EOL_EOL)));
-  }
-}
-
-#endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/help/help_handler.h b/chrome/browser/ui/webui/help/help_handler.h
deleted file mode 100644
index 5aea40c..0000000
--- a/chrome/browser/ui/webui/help/help_handler.h
+++ /dev/null
@@ -1,136 +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_HELP_HELP_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_HELP_HELP_HANDLER_H_
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-#include "chrome/browser/ui/webui/help/version_updater.h"
-#include "chrome/browser/upgrade_observer.h"
-#include "components/policy/core/common/policy_service.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-#if defined(OS_CHROMEOS)
-#include "base/task/cancelable_task_tracker.h"
-#include "chromeos/system/version_loader.h"
-#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
-#endif  // defined(OS_CHROMEOS)
-
-namespace base {
-class DictionaryValue;
-class FilePath;
-class ListValue;
-}
-
-// WebUI message handler for the help page.
-class HelpHandler : public content::WebUIMessageHandler,
-                    public UpgradeObserver {
- public:
-  HelpHandler();
-  ~HelpHandler() override;
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
-  // Adds string values for the UI to |localized_strings|.
-  static void GetLocalizedValues(base::DictionaryValue* localized_strings);
-
-  // UpgradeObserver implementation.
-  void OnUpgradeRecommended() override;
-
-  // Returns the browser version as a string.
-  static base::string16 BuildBrowserVersionString();
-
- private:
-  void OnDeviceAutoUpdatePolicyChanged(const base::Value* previous_policy,
-                                       const base::Value* current_policy);
-
-  // On ChromeOS, this gets the current update status. On other platforms, it
-  // will request and perform an update (if one is available).
-  void RefreshUpdateStatus();
-
-  // Initializes querying values for the page.
-  void OnPageLoaded(const base::ListValue* args);
-
-#if defined(OS_MACOSX)
-  // Promotes the updater for all users.
-  void PromoteUpdater(const base::ListValue* args);
-#endif
-
-  // Relaunches the browser. |args| must be empty.
-  void RelaunchNow(const base::ListValue* args);
-
-  // Opens the feedback dialog. |args| must be empty.
-  void OpenFeedbackDialog(const base::ListValue* args);
-
-  // Opens the help page. |args| must be empty.
-  void OpenHelpPage(const base::ListValue* args);
-
-#if defined(OS_CHROMEOS)
-  // Sets the release track version.
-  void SetChannel(const base::ListValue* args);
-
-  // Performs relaunch and powerwash.
-  void RelaunchAndPowerwash(const base::ListValue* args);
-#endif
-
-  // Checks for and applies update.
-  void RequestUpdate(const base::ListValue* args);
-
-  // Callback method which forwards status updates to the page.
-  void SetUpdateStatus(VersionUpdater::Status status,
-                       int progress,
-                       const std::string& version,
-                       int64_t size,
-                       const base::string16& fail_message);
-
-#if defined(OS_MACOSX)
-  // Callback method which forwards promotion state to the page.
-  void SetPromotionState(VersionUpdater::PromotionState state);
-#endif
-
-#if defined(OS_CHROMEOS)
-  // Callbacks from VersionLoader.
-  void OnOSVersion(const std::string& version);
-  void OnARCVersion(const std::string& firmware);
-  void OnOSFirmware(const std::string& firmware);
-  void OnCurrentChannel(const std::string& channel);
-  void OnTargetChannel(const std::string& channel);
-
-  // Callback for when the directory with the regulatory label image and alt
-  // text has been found.
-  void OnRegulatoryLabelDirFound(const base::FilePath& path);
-
-  // Callback for setting the regulatory label source.
-  void OnRegulatoryLabelImageFound(const base::FilePath& path);
-
-  // Callback for setting the regulatory label alt text.
-  void OnRegulatoryLabelTextRead(const std::string& text);
-
-  // Callback for setting the eol string text.
-  void OnEolStatus(update_engine::EndOfLifeStatus status);
-#endif
-
-  // Specialized instance of the VersionUpdater used to update the browser.
-  std::unique_ptr<VersionUpdater> version_updater_;
-
-  // Used to observe changes in the |kDeviceAutoUpdateDisabled| policy.
-  policy::PolicyChangeRegistrar policy_registrar_;
-
-  // If true changes to UpgradeObserver are applied, if false they are ignored.
-  bool apply_changes_from_upgrade_observer_;
-
-  // Used for callbacks.
-  base::WeakPtrFactory<HelpHandler> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(HelpHandler);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_HELP_HELP_HANDLER_H_
diff --git a/chrome/browser/ui/webui/help/help_utils_chromeos.cc b/chrome/browser/ui/webui/help/help_utils_chromeos.cc
index 068f358..46b3ad2 100644
--- a/chrome/browser/ui/webui/help/help_utils_chromeos.cc
+++ b/chrome/browser/ui/webui/help/help_utils_chromeos.cc
@@ -11,13 +11,8 @@
 #include "base/logging.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/grit/generated_resources.h"
 #include "chromeos/chromeos_switches.h"
-#include "chromeos/network/network_state.h"
-#include "chromeos/network/network_type_pattern.h"
 #include "chromeos/settings/cros_settings_names.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
-#include "ui/base/l10n/l10n_util.h"
 
 namespace help_utils_chromeos {
 
@@ -52,25 +47,4 @@
   return false;
 }
 
-base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network) {
-  const std::string type =
-      network->IsUsingMobileData() ? shill::kTypeCellular : network->type();
-  if (chromeos::NetworkTypePattern::Ethernet().MatchesType(type))
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_ETHERNET);
-  if (type == shill::kTypeWifi)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIFI);
-  if (type == shill::kTypeWimax)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIMAX);
-  if (type == shill::kTypeBluetooth)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_BLUETOOTH);
-  if (type == shill::kTypeCellular ||
-      chromeos::NetworkTypePattern::Tether().MatchesType(type)) {
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_MOBILE_DATA);
-  }
-  if (type == shill::kTypeVPN)
-    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_VPN);
-  NOTREACHED();
-  return base::string16();
-}
-
 }  // namespace help_utils_chromeos
diff --git a/chrome/browser/ui/webui/help/help_utils_chromeos.h b/chrome/browser/ui/webui/help/help_utils_chromeos.h
index ec388ef..c022989 100644
--- a/chrome/browser/ui/webui/help/help_utils_chromeos.h
+++ b/chrome/browser/ui/webui/help/help_utils_chromeos.h
@@ -5,12 +5,6 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_
 #define CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_
 
-#include "base/strings/string16.h"
-
-namespace chromeos {
-class NetworkState;
-}
-
 namespace help_utils_chromeos {
 
 // Returns true if updates over cellular networks are allowed. If |interactive|
@@ -20,9 +14,6 @@
 // policy.
 bool IsUpdateOverCellularAllowed(bool interactive);
 
-// Returns localized name for the connection |type|.
-base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network);
-
 }  // namespace help_utils_chromeos
 
 #endif  // CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index e40bf19..6f1d85c 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
@@ -22,8 +23,10 @@
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_type_pattern.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 
 using chromeos::CrosSettings;
@@ -76,6 +79,27 @@
   return update_disabled;
 }
 
+base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network) {
+  const std::string type =
+      network->IsUsingMobileData() ? shill::kTypeCellular : network->type();
+  if (chromeos::NetworkTypePattern::Ethernet().MatchesType(type))
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_ETHERNET);
+  if (type == shill::kTypeWifi)
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIFI);
+  if (type == shill::kTypeWimax)
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIMAX);
+  if (type == shill::kTypeBluetooth)
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_BLUETOOTH);
+  if (type == shill::kTypeCellular ||
+      chromeos::NetworkTypePattern::Tether().MatchesType(type)) {
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_MOBILE_DATA);
+  }
+  if (type == shill::kTypeVPN)
+    return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_VPN);
+  NOTREACHED();
+  return base::string16();
+}
+
 // Returns whether an update is allowed. If not, it calls the callback with
 // the appropriate status. |interactive| indicates whether the user is actively
 // checking for updates.
@@ -101,8 +125,7 @@
     return false;
   } else if (status == NETWORK_STATUS_DISALLOWED) {
     base::string16 message = l10n_util::GetStringFUTF16(
-        IDS_UPGRADE_DISALLOWED,
-        help_utils_chromeos::GetConnectionTypeAsUTF16(network));
+        IDS_UPGRADE_DISALLOWED, GetConnectionTypeAsUTF16(network));
     callback.Run(VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED, 0,
                  std::string(), 0, message);
     return false;
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 1ea65bf..be1d3c6 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -241,6 +241,7 @@
     {"aboutArcVersionLabel", IDS_SETTINGS_ABOUT_PAGE_ARC_VERSION},
     {"aboutBuildDateLabel", IDS_VERSION_UI_BUILD_DATE},
     {"aboutChannelBeta", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_BETA},
+    {"aboutChannelCanary", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_CANARY},
     {"aboutChannelDev", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_DEV},
     {"aboutChannelLabel", IDS_SETTINGS_ABOUT_PAGE_CHANNEL},
     {"aboutChannelStable", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE},
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index f99019f..11480b8 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -29,6 +29,7 @@
     "elements/close_button_texture.h",
     "elements/content_element.cc",
     "elements/content_element.h",
+    "elements/draw_phase.h",
     "elements/exclusive_screen_toast.cc",
     "elements/exclusive_screen_toast.h",
     "elements/exclusive_screen_toast_texture.cc",
@@ -72,7 +73,7 @@
     "elements/transience_manager.h",
     "elements/ui_element.cc",
     "elements/ui_element.h",
-    "elements/ui_element_debug_id.h",
+    "elements/ui_element_name.h",
     "elements/ui_element_transform_operations.cc",
     "elements/ui_element_transform_operations.h",
     "elements/ui_texture.cc",
diff --git a/chrome/browser/vr/elements/draw_phase.h b/chrome/browser/vr/elements/draw_phase.h
new file mode 100644
index 0000000..742e728
--- /dev/null
+++ b/chrome/browser/vr/elements/draw_phase.h
@@ -0,0 +1,25 @@
+// 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_VR_ELEMENTS_DRAW_PHASE_H_
+#define CHROME_BROWSER_VR_ELEMENTS_DRAW_PHASE_H_
+
+namespace vr {
+
+// Each draw phase is rendered independently in the order specified below.
+//
+// TODO(vollick): once we've established the element hierarchy, only elements in
+// the 2D browsing foreground need to be depth sorted. We should rewrite our
+// sorting logic at that point to leverage the hierarchy and delete this enum.
+enum DrawPhase : int {
+  // kPhaseNone is to be used for elements that do not draw. Eg, layouts.
+  kPhaseBackground = 0,
+  kPhaseFloorCeiling,
+  kPhaseForeground,
+  kPhaseNone,
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_ELEMENTS_DRAW_PHASE_H_
diff --git a/chrome/browser/vr/elements/linear_layout.cc b/chrome/browser/vr/elements/linear_layout.cc
index d5ec7e2f..524cf02 100644
--- a/chrome/browser/vr/elements/linear_layout.cc
+++ b/chrome/browser/vr/elements/linear_layout.cc
@@ -20,13 +20,13 @@
 
 void LinearLayout::LayOutChildren() {
   float total_extent = -margin_;
-  for (auto* child : children()) {
+  for (auto& child : children()) {
     if (child->visible())
       total_extent += GetExtent(*child, direction_) + margin_;
   }
 
   float offset = -0.5 * total_extent;
-  for (auto* child : children()) {
+  for (auto& child : children()) {
     if (!child->visible())
       continue;
     float extent = GetExtent(*child, direction_);
diff --git a/chrome/browser/vr/elements/linear_layout_unittest.cc b/chrome/browser/vr/elements/linear_layout_unittest.cc
index 833ce0d..d6c0cb07 100644
--- a/chrome/browser/vr/elements/linear_layout_unittest.cc
+++ b/chrome/browser/vr/elements/linear_layout_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/vr/elements/linear_layout.h"
 
+#include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace vr {
@@ -11,27 +12,29 @@
 TEST(LinearLayout, HorizontalLayout) {
   LinearLayout layout(LinearLayout::kHorizontal);
   layout.set_margin(10);
-  UiElement rect_a;
-  rect_a.SetSize(10, 10);
-  rect_a.SetVisible(true);
-  layout.AddChild(&rect_a);
+  auto element = base::MakeUnique<UiElement>();
+  UiElement* rect_a = element.get();
+  rect_a->SetSize(10, 10);
+  rect_a->SetVisible(true);
+  layout.AddChild(std::move(element));
 
   // One element should require no position adjustment at all.
   layout.LayOutChildren();
-  EXPECT_TRUE(rect_a.LocalTransform().IsIdentity());
+  EXPECT_TRUE(rect_a->LocalTransform().IsIdentity());
 
   // Two elements should be centered and separated by the margin.
-  UiElement rect_b;
-  rect_b.SetSize(20, 20);
-  rect_b.SetVisible(true);
-  layout.AddChild(&rect_b);
+  element = base::MakeUnique<UiElement>();
+  UiElement* rect_b = element.get();
+  rect_b->SetSize(20, 20);
+  rect_b->SetVisible(true);
+  layout.AddChild(std::move(element));
   layout.LayOutChildren();
 
   gfx::Point3F position_a;
-  rect_a.LocalTransform().TransformPoint(&position_a);
+  rect_a->LocalTransform().TransformPoint(&position_a);
 
   gfx::Point3F position_b;
-  rect_b.LocalTransform().TransformPoint(&position_b);
+  rect_b->LocalTransform().TransformPoint(&position_b);
 
   EXPECT_FLOAT_EQ(-15.0f, position_a.x());
   EXPECT_FLOAT_EQ(0.0f, position_a.y());
@@ -41,36 +44,38 @@
   EXPECT_FLOAT_EQ(0.0f, position_b.y());
   EXPECT_FLOAT_EQ(0.0f, position_b.z());
 
-  rect_a.SetVisible(false);
+  rect_a->SetVisible(false);
   layout.LayOutChildren();
   // The invisible child should not be accounted for in the layout.
-  EXPECT_TRUE(rect_b.LocalTransform().IsIdentity());
+  EXPECT_TRUE(rect_b->LocalTransform().IsIdentity());
 }
 
 TEST(LinearLayout, VerticalLayout) {
   LinearLayout layout(LinearLayout::kVertical);
   layout.set_margin(10);
-  UiElement rect_a;
-  rect_a.SetSize(10, 10);
-  rect_a.SetVisible(true);
-  layout.AddChild(&rect_a);
+  auto element = base::MakeUnique<UiElement>();
+  UiElement* rect_a = element.get();
+  rect_a->SetSize(10, 10);
+  rect_a->SetVisible(true);
+  layout.AddChild(std::move(element));
 
   // One element should require no position adjustment at all.
   layout.LayOutChildren();
-  EXPECT_TRUE(rect_a.LocalTransform().IsIdentity());
+  EXPECT_TRUE(rect_a->LocalTransform().IsIdentity());
 
   // Two elements should be centered and separated by the margin.
-  UiElement rect_b;
-  rect_b.SetSize(20, 20);
-  rect_b.SetVisible(true);
-  layout.AddChild(&rect_b);
+  element = base::MakeUnique<UiElement>();
+  UiElement* rect_b = element.get();
+  rect_b->SetSize(20, 20);
+  rect_b->SetVisible(true);
+  layout.AddChild(std::move(element));
   layout.LayOutChildren();
 
   gfx::Point3F position_a;
-  rect_a.LocalTransform().TransformPoint(&position_a);
+  rect_a->LocalTransform().TransformPoint(&position_a);
 
   gfx::Point3F position_b;
-  rect_b.LocalTransform().TransformPoint(&position_b);
+  rect_b->LocalTransform().TransformPoint(&position_b);
 
   EXPECT_FLOAT_EQ(0.0f, position_a.x());
   EXPECT_FLOAT_EQ(-15.0f, position_a.y());
@@ -80,10 +85,10 @@
   EXPECT_FLOAT_EQ(10.0f, position_b.y());
   EXPECT_FLOAT_EQ(0.0f, position_b.z());
 
-  rect_a.SetVisible(false);
+  rect_a->SetVisible(false);
   layout.LayOutChildren();
   // The invisible child should not be accounted for in the layout.
-  EXPECT_TRUE(rect_b.LocalTransform().IsIdentity());
+  EXPECT_TRUE(rect_b->LocalTransform().IsIdentity());
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/elements/ui_element.cc b/chrome/browser/vr/elements/ui_element.cc
index 442c209..c3eddc8 100644
--- a/chrome/browser/vr/elements/ui_element.cc
+++ b/chrome/browser/vr/elements/ui_element.cc
@@ -7,6 +7,7 @@
 #include <limits>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/time/time.h"
 #include "cc/base/math_util.h"
 #include "chrome/browser/vr/elements/ui_element_transform_operations.h"
@@ -16,6 +17,11 @@
 
 namespace {
 
+int AllocateId() {
+  static int g_next_id = 1;
+  return g_next_id++;
+}
+
 bool GetRayPlaneDistance(const gfx::Point3F& ray_origin,
                          const gfx::Vector3dF& ray_vector,
                          const gfx::Point3F& plane_origin,
@@ -32,7 +38,7 @@
 
 }  // namespace
 
-UiElement::UiElement() {
+UiElement::UiElement() : id_(AllocateId()) {
   animation_player_.set_target(this);
   layout_offset_.AppendTranslate(0, 0, 0);
   transform_operations_.AppendTranslate(0, 0, 0);
@@ -158,6 +164,9 @@
 }
 
 void UiElement::SetMode(ColorScheme::Mode mode) {
+  for (auto& child : children_) {
+    child->SetMode(mode);
+  }
   if (mode_ == mode)
     return;
   mode_ = mode;
@@ -166,9 +175,20 @@
 
 void UiElement::OnSetMode() {}
 
-void UiElement::AddChild(UiElement* child) {
+void UiElement::AddChild(std::unique_ptr<UiElement> child) {
   child->parent_ = this;
-  children_.push_back(child);
+  children_.push_back(std::move(child));
+}
+
+void UiElement::RemoveChild(UiElement* to_remove) {
+  DCHECK_EQ(this, to_remove->parent_);
+  to_remove->parent_ = nullptr;
+  size_t old_size = children_.size();
+  base::EraseIf(children_,
+                [to_remove](const std::unique_ptr<UiElement>& child) {
+                  return child.get() == to_remove;
+                });
+  DCHECK_NE(old_size, children_.size());
 }
 
 gfx::Point3F UiElement::GetCenter() const {
@@ -243,7 +263,7 @@
 }
 
 void UiElement::LayOutChildren() {
-  for (auto* child : children_) {
+  for (auto& child : children_) {
     // To anchor a child, use the parent's size to find its edge.
     float x_offset;
     switch (child->x_anchoring()) {
diff --git a/chrome/browser/vr/elements/ui_element.h b/chrome/browser/vr/elements/ui_element.h
index 0a32557..1cecdef 100644
--- a/chrome/browser/vr/elements/ui_element.h
+++ b/chrome/browser/vr/elements/ui_element.h
@@ -14,7 +14,8 @@
 #include "cc/animation/transform_operations.h"
 #include "chrome/browser/vr/animation_player.h"
 #include "chrome/browser/vr/color_scheme.h"
-#include "chrome/browser/vr/elements/ui_element_debug_id.h"
+#include "chrome/browser/vr/elements/draw_phase.h"
+#include "chrome/browser/vr/elements/ui_element_name.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/quaternion.h"
@@ -101,7 +102,6 @@
   virtual bool HitTest(const gfx::PointF& point) const;
 
   int id() const { return id_; }
-  void set_id(int id) { id_ = id; }
 
   // If true, this object will be visible.
   bool visible() const { return visible_; }
@@ -187,8 +187,8 @@
   void set_dirty(bool dirty) { dirty_ = dirty; }
 
   // A flag usable during transformation calculates to avoid duplicate work.
-  UiElementDebugId debug_id() const { return debug_id_; }
-  void set_debug_id(UiElementDebugId debug_id) { debug_id_ = debug_id; }
+  UiElementName name() const { return name_; }
+  void set_name(UiElementName name) { name_ = name; }
 
   // By default, sets an element to be visible or not. This may be overridden to
   // allow finer control of element visibility.
@@ -205,10 +205,9 @@
   }
 
   // Transformations are applied relative to the parent element, rather than
-  // absolutely. You cannot currently unparent elements.
-  // TODO(vollick): elements should own their children. UiScene can turn into
-  // recursive operations on the UiElement tree.
-  void AddChild(UiElement* child);
+  // absolutely.
+  void AddChild(std::unique_ptr<UiElement> child);
+  void RemoveChild(UiElement* child);
   UiElement* parent() { return parent_; }
 
   gfx::Point3F GetCenter() const;
@@ -258,11 +257,14 @@
   // or right where the head is pointing.
   virtual void AdjustRotationForHeadPose(const gfx::Vector3dF& look_at);
 
+  std::vector<std::unique_ptr<UiElement>>& children() { return children_; }
+  const std::vector<std::unique_ptr<UiElement>>& children() const {
+    return children_;
+  }
+
  protected:
   virtual void OnSetMode();
 
-  std::vector<UiElement*>& children() { return children_; }
-
   base::TimeTicks last_frame_time() const { return last_frame_time_; }
 
  private:
@@ -306,7 +308,7 @@
 
   AnimationPlayer animation_player_;
 
-  int draw_phase_ = -1;
+  int draw_phase_ = kPhaseNone;
 
   // This is the time as of the last call to |Animate|. It is needed when
   // reversing transitions.
@@ -319,7 +321,7 @@
   bool dirty_ = false;
 
   // An identifier used for testing and debugging, in lieu of a string.
-  UiElementDebugId debug_id_ = UiElementDebugId::kNone;
+  UiElementName name_ = UiElementName::kNone;
 
   // This local transform operations. They are inherited by descendants and are
   // stored as a list of operations rather than a baked transform to make
@@ -337,7 +339,7 @@
   ColorScheme::Mode mode_ = ColorScheme::kModeNormal;
 
   UiElement* parent_ = nullptr;
-  std::vector<UiElement*> children_;
+  std::vector<std::unique_ptr<UiElement>> children_;
 
   DISALLOW_COPY_AND_ASSIGN(UiElement);
 };
diff --git a/chrome/browser/vr/elements/ui_element_debug_id.h b/chrome/browser/vr/elements/ui_element_name.h
similarity index 67%
rename from chrome/browser/vr/elements/ui_element_debug_id.h
rename to chrome/browser/vr/elements/ui_element_name.h
index ee334a5..eca211a 100644
--- a/chrome/browser/vr/elements/ui_element_debug_id.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -2,40 +2,44 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_DEBUG_ID_H_
-#define CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_DEBUG_ID_H_
+#ifndef CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_NAME_H_
+#define CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_NAME_H_
 
 namespace vr {
 
 // These identifiers serve as stable, semantic identifiers for UI elements.
-//
-// TODO(vollick): This should become UiElementName. These identifiers will be
-// useful outside of testing and debugging code. Named as it is today, it sounds
-// as if this can be done away with in release builds, which is not true.
-enum UiElementDebugId {
+enum UiElementName {
   kNone = 0,
+  kRoot,
+  k2dBrowsingRoot,
+  k2dBrowsingBackground,
+  k2dBrowsingForeground,
+  k2dBrowsingViewportAwareRoot,
+  kWebVrRoot,
   kWebVrPermanentHttpSecurityWarning,
   kWebVrTransientHttpSecurityWarning,
+  kWebVrViewportAwareRoot,
   kContentQuad,
   kBackplane,
   kCeiling,
   kFloor,
   kUrlBar,
-  kLoadingIndicator,
+  kIndicatorLayout,
   kAudioCaptureIndicator,
   kVideoCaptureIndicator,
   kScreenCaptureIndicator,
+  kLocationAccessIndicator,
+  kBluetoothConnectedIndicator,
+  kLoadingIndicator,
   kCloseButton,
   kScreenDimmer,
   kExitWarning,
   kExitPrompt,
   kExitPromptBackplane,
   kWebVrUrlToast,
-  kLocationAccessIndicator,
   kExclusiveScreenToast,
   kExclusiveScreenToastViewportAware,
   kSplashScreenText,
-  kBluetoothConnectedIndicator,
   kBackgroundFront,
   kBackgroundLeft,
   kBackgroundBack,
@@ -47,4 +51,4 @@
 
 }  // namespace vr
 
-#endif  // CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_DEBUG_ID_H_
+#endif  // CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_NAME_H_
diff --git a/chrome/browser/vr/elements/viewport_aware_root.cc b/chrome/browser/vr/elements/viewport_aware_root.cc
index 687d9f7..c6b5f1c 100644
--- a/chrome/browser/vr/elements/viewport_aware_root.cc
+++ b/chrome/browser/vr/elements/viewport_aware_root.cc
@@ -19,8 +19,16 @@
 
 void ViewportAwareRoot::AdjustRotationForHeadPose(
     const gfx::Vector3dF& look_at) {
-  // This must be a top level element.
-  DCHECK(!parent());
+  // We must not inherit a transform.
+  //
+  // TODO(vollick): ensure that this check happens at the right time in the
+  // frame lifecycle. More precisely, once we've made ApplyRecursiveTransforms a
+  // recursive function on the UiElement tree, we can do this check there.
+  // Presently, we're checking our parent's transform from last frame. The
+  // transform from last frame should also be the identity, of course, so the
+  // check is valid, but we'd prefer to check the transform for the current
+  // frame.
+  DCHECK(parent()->world_space_transform().IsIdentity());
   DCHECK(viewport_aware());
 
   gfx::Vector3dF rotated_center_vector{0.f, 0.f, -1.0f};
diff --git a/chrome/browser/vr/elements/viewport_aware_root_unittest.cc b/chrome/browser/vr/elements/viewport_aware_root_unittest.cc
index 5bb1b9f..17f96dc 100644
--- a/chrome/browser/vr/elements/viewport_aware_root_unittest.cc
+++ b/chrome/browser/vr/elements/viewport_aware_root_unittest.cc
@@ -7,6 +7,7 @@
 #include <cmath>
 
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/vr/elements/draw_phase.h"
 #include "chrome/browser/vr/test/animation_utils.h"
 #include "chrome/browser/vr/test/constants.h"
 #include "chrome/browser/vr/ui_scene.h"
@@ -50,7 +51,11 @@
 }
 
 void CheckRotateClockwiseAndReverse(const gfx::Vector3dF& initial_look_at) {
-  ViewportAwareRoot root;
+  UiScene scene;
+
+  auto element = base::MakeUnique<ViewportAwareRoot>();
+  ViewportAwareRoot& root = *element;
+  scene.AddUiElement(kRoot, std::move(element));
   gfx::Vector3dF look_at(initial_look_at);
   gfx::Transform expected;
 
@@ -108,29 +113,28 @@
 TEST(ViewportAwareRoot, ChildElementsRepositioned) {
   UiScene scene;
 
-  auto root = base::MakeUnique<ViewportAwareRoot>();
-  root->set_id(0);
-  root->set_draw_phase(0);
-  scene.AddUiElement(std::move(root));
+  auto viewport_aware_root = base::MakeUnique<ViewportAwareRoot>();
+  ViewportAwareRoot& root = *viewport_aware_root;
+  root.set_draw_phase(kPhaseForeground);
+  scene.AddUiElement(kRoot, std::move(viewport_aware_root));
 
   auto element = base::MakeUnique<UiElement>();
-  element->set_id(1);
+  UiElement& child = *element;
   element->set_viewport_aware(true);
-  element->set_draw_phase(0);
+  element->set_draw_phase(kPhaseForeground);
   element->SetTranslate(0.f, 0.f, -1.f);
-  scene.GetUiElementById(0)->AddChild(element.get());
-  scene.AddUiElement(std::move(element));
+  root.AddChild(std::move(element));
 
   gfx::Vector3dF look_at{0.f, 0.f, -1.f};
   scene.OnBeginFrame(MicrosecondsToTicks(0), look_at);
-  EXPECT_TRUE(Point3FAreNearlyEqual(gfx::Point3F(0.f, 0.f, -1.f),
-                                    scene.GetUiElementById(1)->GetCenter()));
+  EXPECT_TRUE(
+      Point3FAreNearlyEqual(gfx::Point3F(0.f, 0.f, -1.f), child.GetCenter()));
 
   // This should trigger reposition of viewport aware elements.
   RotateAboutYAxis(90.f, &look_at);
   scene.OnBeginFrame(MicrosecondsToTicks(10), look_at);
-  EXPECT_TRUE(Point3FAreNearlyEqual(gfx::Point3F(-1.f, 0.f, 0.f),
-                                    scene.GetUiElementById(1)->GetCenter()));
+  EXPECT_TRUE(
+      Point3FAreNearlyEqual(gfx::Point3F(-1.f, 0.f, 0.f), child.GetCenter()));
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/test/ui_scene_manager_test.cc b/chrome/browser/vr/test/ui_scene_manager_test.cc
index b46a666..5aacfd3 100644
--- a/chrome/browser/vr/test/ui_scene_manager_test.cc
+++ b/chrome/browser/vr/test/ui_scene_manager_test.cc
@@ -33,29 +33,32 @@
       kNotInWebVr, kAutopresented);
 }
 
-bool UiSceneManagerTest::IsVisible(UiElementDebugId debug_id) const {
-  UiElement* element = scene_->GetUiElementByDebugId(debug_id);
+bool UiSceneManagerTest::IsVisible(UiElementName name) const {
+  UiElement* element = scene_->GetUiElementByName(name);
   return element ? element->visible() : false;
 }
 
 void UiSceneManagerTest::VerifyElementsVisible(
     const std::string& debug_name,
-    const std::set<UiElementDebugId>& debug_ids) const {
+    const std::set<UiElementName>& names) const {
   SCOPED_TRACE(debug_name);
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible =
-        debug_ids.find(element->debug_id()) != debug_ids.end();
-    EXPECT_EQ(should_be_visible, element->visible());
+  for (auto name : names) {
+    SCOPED_TRACE(name);
+    auto* element = scene_->GetUiElementByName(name);
+    EXPECT_NE(nullptr, element);
+    EXPECT_TRUE(element->visible());
   }
 }
 
-bool UiSceneManagerTest::VerifyVisibility(
-    const std::set<UiElementDebugId>& debug_ids,
-    bool visible) const {
-  for (const auto& element : scene_->GetUiElements()) {
-    if (debug_ids.find(element->debug_id()) != debug_ids.end() &&
-        element->visible() != visible) {
+bool UiSceneManagerTest::VerifyVisibility(const std::set<UiElementName>& names,
+                                          bool visible) const {
+  for (auto name : names) {
+    SCOPED_TRACE(name);
+    auto* element = scene_->GetUiElementByName(name);
+    if (!element && visible) {
+      return false;
+    }
+    if (element && element->visible() != visible) {
       return false;
     }
   }
@@ -83,7 +86,7 @@
 
 SkColor UiSceneManagerTest::GetBackgroundColor() const {
   Rect* front =
-      static_cast<Rect*>(scene_->GetUiElementByDebugId(kBackgroundFront));
+      static_cast<Rect*>(scene_->GetUiElementByName(kBackgroundFront));
   EXPECT_NE(nullptr, front);
   if (!front)
     return SK_ColorBLACK;
@@ -92,10 +95,9 @@
 
   // While returning background color, ensure that all background panel elements
   // share the same color.
-  for (auto debug_id : {kBackgroundFront, kBackgroundLeft, kBackgroundBack,
-                        kBackgroundRight, kBackgroundTop, kBackgroundBottom}) {
-    const Rect* panel =
-        static_cast<Rect*>(scene_->GetUiElementByDebugId(debug_id));
+  for (auto name : {kBackgroundFront, kBackgroundLeft, kBackgroundBack,
+                    kBackgroundRight, kBackgroundTop, kBackgroundBottom}) {
+    const Rect* panel = static_cast<Rect*>(scene_->GetUiElementByName(name));
     EXPECT_NE(nullptr, panel);
     if (!panel)
       return SK_ColorBLACK;
diff --git a/chrome/browser/vr/test/ui_scene_manager_test.h b/chrome/browser/vr/test/ui_scene_manager_test.h
index 2eb756c..b7f19bc 100644
--- a/chrome/browser/vr/test/ui_scene_manager_test.h
+++ b/chrome/browser/vr/test/ui_scene_manager_test.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "chrome/browser/vr/elements/ui_element_debug_id.h"
+#include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/test/mock_browser_interface.h"
 #include "chrome/browser/vr/test/mock_content_input_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -47,15 +47,15 @@
   void MakeManager(InCct in_cct, InWebVr in_web_vr);
   void MakeAutoPresentedManager();
 
-  bool IsVisible(UiElementDebugId debug_id) const;
+  bool IsVisible(UiElementName name) const;
 
   // Verify that only the elements in the set are visible.
   void VerifyElementsVisible(const std::string& debug_name,
-                             const std::set<UiElementDebugId>& debug_ids) const;
+                             const std::set<UiElementName>& names) const;
 
   // Return false if not all elements in the set match the specified visibility
   // state. Other elements are ignored.
-  bool VerifyVisibility(const std::set<UiElementDebugId>& debug_ids,
+  bool VerifyVisibility(const std::set<UiElementName>& names,
                         bool visible) const;
 
   // Advances current_time_ by delta. This is done in frame increments and
diff --git a/chrome/browser/vr/ui_input_manager.cc b/chrome/browser/vr/ui_input_manager.cc
index ac1f0438..8d3b0f9 100644
--- a/chrome/browser/vr/ui_input_manager.cc
+++ b/chrome/browser/vr/ui_input_manager.cc
@@ -44,6 +44,65 @@
   return false;
 }
 
+bool GetTargetLocalPoint(const gfx::Vector3dF& eye_to_target,
+                         const UiElement& element,
+                         float max_distance_to_plane,
+                         gfx::PointF* out_target_local_point,
+                         gfx::Point3F* out_target_point,
+                         float* out_distance_to_plane) {
+  if (!element.GetRayDistance(kOrigin, eye_to_target, out_distance_to_plane)) {
+    return false;
+  }
+
+  if (*out_distance_to_plane < 0 ||
+      *out_distance_to_plane >= max_distance_to_plane) {
+    return false;
+  }
+
+  *out_target_point =
+      GetRayPoint(kOrigin, eye_to_target, *out_distance_to_plane);
+  gfx::PointF unit_xy_point =
+      element.GetUnitRectangleCoordinates(*out_target_point);
+
+  out_target_local_point->set_x(0.5f + unit_xy_point.x());
+  out_target_local_point->set_y(0.5f - unit_xy_point.y());
+  return true;
+}
+
+void HitTestElements(UiElement* element,
+                     gfx::Vector3dF* out_eye_to_target,
+                     float* out_closest_element_distance,
+                     gfx::Point3F* out_target_point,
+                     UiElement** out_target_element,
+                     gfx::PointF* out_target_local_point) {
+  for (auto& child : element->children()) {
+    HitTestElements(child.get(), out_eye_to_target,
+                    out_closest_element_distance, out_target_point,
+                    out_target_element, out_target_local_point);
+  }
+
+  if (!element->IsHitTestable()) {
+    return;
+  }
+
+  gfx::PointF local_point;
+  gfx::Point3F plane_intersection_point;
+  float distance_to_plane;
+  if (!GetTargetLocalPoint(*out_eye_to_target, *element,
+                           *out_closest_element_distance, &local_point,
+                           &plane_intersection_point, &distance_to_plane)) {
+    return;
+  }
+  if (!element->HitTest(local_point)) {
+    return;
+  }
+
+  *out_closest_element_distance = distance_to_plane;
+  *out_target_point = plane_intersection_point;
+  *out_target_element = element;
+  *out_target_local_point = local_point;
+}
+
 }  // namespace
 
 UiInputManager::UiInputManager(UiScene* scene) : scene_(scene) {}
@@ -236,7 +295,7 @@
   // moves for how noisy the controller is. It's almost impossible to click a
   // link without unintentionally starting a drag event. For this reason we
   // disable mouse moves, only delivering a down and up event.
-  if (hover_target_->debug_id() == kContentQuad && in_click_) {
+  if (hover_target_->name() == kContentQuad && in_click_) {
     return;
   }
 
@@ -311,52 +370,9 @@
   // and the controller target position.
   float closest_element_distance = (*out_target_point - kOrigin).Length();
 
-  for (auto& element : scene_->GetUiElements()) {
-    if (!element->IsHitTestable()) {
-      continue;
-    }
-    gfx::PointF local_point;
-    gfx::Point3F plane_intersection_point;
-    float distance_to_plane;
-    if (!GetTargetLocalPoint(*out_eye_to_target, *element.get(),
-                             closest_element_distance, &local_point,
-                             &plane_intersection_point, &distance_to_plane)) {
-      continue;
-    }
-    if (!element->HitTest(local_point)) {
-      continue;
-    }
-
-    closest_element_distance = distance_to_plane;
-    *out_target_point = plane_intersection_point;
-    *out_target_element = element.get();
-    *out_target_local_point = local_point;
-  }
-}
-
-bool UiInputManager::GetTargetLocalPoint(const gfx::Vector3dF& eye_to_target,
-                                         const UiElement& element,
-                                         float max_distance_to_plane,
-                                         gfx::PointF* out_target_local_point,
-                                         gfx::Point3F* out_target_point,
-                                         float* out_distance_to_plane) const {
-  if (!element.GetRayDistance(kOrigin, eye_to_target, out_distance_to_plane)) {
-    return false;
-  }
-
-  if (*out_distance_to_plane < 0 ||
-      *out_distance_to_plane >= max_distance_to_plane) {
-    return false;
-  }
-
-  *out_target_point =
-      GetRayPoint(kOrigin, eye_to_target, *out_distance_to_plane);
-  gfx::PointF unit_xy_point =
-      element.GetUnitRectangleCoordinates(*out_target_point);
-
-  out_target_local_point->set_x(0.5f + unit_xy_point.x());
-  out_target_local_point->set_y(0.5f - unit_xy_point.y());
-  return true;
+  HitTestElements(&scene_->root_element(), out_eye_to_target,
+                  &closest_element_distance, out_target_point,
+                  out_target_element, out_target_local_point);
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_input_manager.h b/chrome/browser/vr/ui_input_manager.h
index ce8bdb4..e00578b 100644
--- a/chrome/browser/vr/ui_input_manager.h
+++ b/chrome/browser/vr/ui_input_manager.h
@@ -69,12 +69,6 @@
                               gfx::Point3F* out_target_point,
                               UiElement** out_target_element,
                               gfx::PointF* out_target_local_point) const;
-  bool GetTargetLocalPoint(const gfx::Vector3dF& eye_to_target,
-                           const UiElement& element,
-                           float max_distance_to_plane,
-                           gfx::PointF* out_target_local_point,
-                           gfx::Point3F* out_target_point,
-                           float* out_distance_to_plane) const;
 
   UiScene* scene_;
   // TODO(mthiesse): We need to handle elements being removed, and update this
diff --git a/chrome/browser/vr/ui_input_manager_unittest.cc b/chrome/browser/vr/ui_input_manager_unittest.cc
index d9a22d8..c2a0f6e9 100644
--- a/chrome/browser/vr/ui_input_manager_unittest.cc
+++ b/chrome/browser/vr/ui_input_manager_unittest.cc
@@ -38,7 +38,7 @@
   // mock out the underlying sensor data. For now, we will hallucinate
   // parameters to HandleInput.
   UiElement* content_quad =
-      scene_->GetUiElementByDebugId(UiElementDebugId::kContentQuad);
+      scene_->GetUiElementByName(UiElementName::kContentQuad);
   gfx::Point3F content_quad_center;
   content_quad->world_space_transform().TransformPoint(&content_quad_center);
   gfx::Point3F origin;
@@ -51,8 +51,7 @@
 
   // We should have hit the content quad if our math was correct.
   ASSERT_NE(nullptr, out_reticle_render_target);
-  EXPECT_EQ(UiElementDebugId::kContentQuad,
-            out_reticle_render_target->debug_id());
+  EXPECT_EQ(UiElementName::kContentQuad, out_reticle_render_target->name());
 
   // Unless we suppress content move events during clicks, this will cause us to
   // call OnContentMove on the delegate. We should do this suppression, so we
diff --git a/chrome/browser/vr/ui_scene.cc b/chrome/browser/vr/ui_scene.cc
index 5df9ee2..a6af243 100644
--- a/chrome/browser/vr/ui_scene.cc
+++ b/chrome/browser/vr/ui_scene.cc
@@ -10,31 +10,48 @@
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/vr/elements/draw_phase.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
 
-void UiScene::AddUiElement(std::unique_ptr<UiElement> element) {
+template <typename F>
+void ForAllElements(UiElement* e, F f) {
+  f(e);
+  for (auto& child : e->children()) {
+    ForAllElements(child.get(), f);
+  }
+}
+
+template <typename P>
+UiElement* FindElement(UiElement* e, P predicate) {
+  if (predicate(e)) {
+    return e;
+  }
+  for (const auto& child : e->children()) {
+    if (UiElement* match = FindElement(child.get(), predicate)) {
+      return match;
+    }
+  }
+  return nullptr;
+}
+
+void UiScene::AddUiElement(UiElementName parent,
+                           std::unique_ptr<UiElement> element) {
   CHECK_GE(element->id(), 0);
   CHECK_EQ(GetUiElementById(element->id()), nullptr);
   CHECK_GE(element->draw_phase(), 0);
-  if (!element->parent()) {
-    CHECK_EQ(element->x_anchoring(), XAnchoring::XNONE);
-    CHECK_EQ(element->y_anchoring(), YAnchoring::YNONE);
-  }
   if (gl_initialized_)
     element->Initialize();
-  ui_elements_.push_back(std::move(element));
+  GetUiElementByName(parent)->AddChild(std::move(element));
 }
 
 void UiScene::RemoveUiElement(int element_id) {
-  for (auto it = ui_elements_.begin(); it != ui_elements_.end(); ++it) {
-    if ((*it)->id() == element_id) {
-      ui_elements_.erase(it);
-      return;
-    }
-  }
+  UiElement* to_remove = GetUiElementById(element_id);
+  CHECK_NE(nullptr, to_remove);
+  CHECK_NE(nullptr, to_remove->parent());
+  to_remove->parent()->RemoveChild(to_remove);
 }
 
 void UiScene::AddAnimation(int element_id,
@@ -50,83 +67,84 @@
 
 void UiScene::OnBeginFrame(const base::TimeTicks& current_time,
                            const gfx::Vector3dF& look_at) {
-  for (const auto& element : ui_elements_) {
+  ForAllElements(root_element_.get(), [current_time](UiElement* element) {
     // Process all animations before calculating object transforms.
     element->Animate(current_time);
     element->set_dirty(true);
-  }
-  for (auto& element : ui_elements_) {
+  });
+
+  ForAllElements(root_element_.get(), [look_at](UiElement* element) {
     element->LayOutChildren();
     element->AdjustRotationForHeadPose(look_at);
-  }
-  for (auto& element : ui_elements_) {
-    ApplyRecursiveTransforms(element.get());
-  }
+  });
+
+  ForAllElements(root_element_.get(), [this](UiElement* element) {
+    this->ApplyRecursiveTransforms(element);
+  });
 }
 
 void UiScene::PrepareToDraw() {
-  for (const auto& element : ui_elements_) {
-    element->PrepareToDraw();
-  }
+  ForAllElements(root_element_.get(),
+                 [](UiElement* element) { element->PrepareToDraw(); });
+}
+
+UiElement& UiScene::root_element() {
+  return *root_element_;
 }
 
 UiElement* UiScene::GetUiElementById(int element_id) const {
-  for (const auto& element : ui_elements_) {
-    if (element->id() == element_id) {
-      return element.get();
-    }
-  }
-  return nullptr;
+  return FindElement(root_element_.get(), [element_id](UiElement* element) {
+    return element->id() == element_id;
+  });
 }
 
-UiElement* UiScene::GetUiElementByDebugId(UiElementDebugId debug_id) const {
-  DCHECK(debug_id != UiElementDebugId::kNone);
-  for (const auto& element : ui_elements_) {
-    if (element->debug_id() == debug_id) {
-      return element.get();
-    }
-  }
-  return nullptr;
+UiElement* UiScene::GetUiElementByName(UiElementName name) const {
+  DCHECK(name != UiElementName::kNone);
+  return FindElement(root_element_.get(), [name](UiElement* element) {
+    return element->name() == name;
+  });
 }
 
 std::vector<const UiElement*> UiScene::GetWorldElements() const {
   std::vector<const UiElement*> elements;
-  for (const auto& element : ui_elements_) {
+  ForAllElements(root_element_.get(), [&elements](UiElement* element) {
     if (element->IsVisible() && !element->viewport_aware() &&
         !element->is_overlay()) {
-      elements.push_back(element.get());
+      elements.push_back(element);
     }
-  }
+  });
   return elements;
 }
 
 std::vector<const UiElement*> UiScene::GetOverlayElements() const {
   std::vector<const UiElement*> elements;
-  for (const auto& element : ui_elements_) {
+  ForAllElements(root_element_.get(), [&elements](UiElement* element) {
     if (element->IsVisible() && element->is_overlay()) {
-      elements.push_back(element.get());
+      elements.push_back(element);
     }
-  }
+  });
   return elements;
 }
 
 std::vector<const UiElement*> UiScene::GetViewportAwareElements() const {
   std::vector<const UiElement*> elements;
-  for (const auto& element : ui_elements_) {
+  ForAllElements(root_element_.get(), [&elements](UiElement* element) {
     if (element->IsVisible() && element->viewport_aware() &&
         element->parent()) {
-      elements.push_back(element.get());
+      elements.push_back(element);
     }
-  }
+  });
   return elements;
 }
 
-const std::vector<std::unique_ptr<UiElement>>& UiScene::GetUiElements() const {
-  return ui_elements_;
+UiScene::UiScene() {
+  root_element_ = base::MakeUnique<UiElement>();
+  root_element_->set_name(kRoot);
+  root_element_->set_draw_phase(kPhaseNone);
+  root_element_->SetVisible(false);
+  root_element_->set_hit_testable(false);
 }
 
-UiScene::UiScene() = default;
-
 UiScene::~UiScene() = default;
 
 void UiScene::ApplyRecursiveTransforms(UiElement* element) {
@@ -159,12 +177,12 @@
   element->set_dirty(false);
 }
 
-// TODO(mthiesse): Move this to UiSceneManager.
+// TODO(vollick): we should bind to gl-initialized state. Elements will
+// initialize when the binding fires, automatically.
 void UiScene::OnGlInitialized() {
   gl_initialized_ = true;
-  for (auto& element : ui_elements_) {
-    element->Initialize();
-  }
+  ForAllElements(root_element_.get(),
+                 [](UiElement* element) { element->Initialize(); });
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_scene.h b/chrome/browser/vr/ui_scene.h
index f0a8947..70967f4b 100644
--- a/chrome/browser/vr/ui_scene.h
+++ b/chrome/browser/vr/ui_scene.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/vr/color_scheme.h"
-#include "chrome/browser/vr/elements/ui_element_debug_id.h"
+#include "chrome/browser/vr/elements/ui_element_name.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace base {
@@ -44,7 +44,7 @@
   UiScene();
   virtual ~UiScene();
 
-  void AddUiElement(std::unique_ptr<UiElement> element);
+  void AddUiElement(UiElementName parent, std::unique_ptr<UiElement> element);
 
   void RemoveUiElement(int element_id);
 
@@ -64,10 +64,10 @@
   // frame lifecycle. After this function, no element should be dirtied.
   void PrepareToDraw();
 
-  const std::vector<std::unique_ptr<UiElement>>& GetUiElements() const;
+  UiElement& root_element();
 
   UiElement* GetUiElementById(int element_id) const;
-  UiElement* GetUiElementByDebugId(UiElementDebugId debug_id) const;
+  UiElement* GetUiElementByName(UiElementName name) const;
 
   std::vector<const UiElement*> GetWorldElements() const;
   std::vector<const UiElement*> GetOverlayElements() const;
@@ -99,7 +99,7 @@
   void Animate(const base::TimeTicks& current_time);
   void ApplyRecursiveTransforms(UiElement* element);
 
-  std::vector<std::unique_ptr<UiElement>> ui_elements_;
+  std::unique_ptr<UiElement> root_element_;
   ColorScheme::Mode mode_ = ColorScheme::kModeNormal;
 
   float background_distance_ = 10.0f;
diff --git a/chrome/browser/vr/ui_scene_manager.cc b/chrome/browser/vr/ui_scene_manager.cc
index ad33866..4abfefd 100644
--- a/chrome/browser/vr/ui_scene_manager.cc
+++ b/chrome/browser/vr/ui_scene_manager.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/vr/elements/button.h"
 #include "chrome/browser/vr/elements/close_button_texture.h"
 #include "chrome/browser/vr/elements/content_element.h"
+#include "chrome/browser/vr/elements/draw_phase.h"
 #include "chrome/browser/vr/elements/exclusive_screen_toast.h"
 #include "chrome/browser/vr/elements/exit_prompt.h"
 #include "chrome/browser/vr/elements/exit_prompt_backplane.h"
@@ -21,7 +22,7 @@
 #include "chrome/browser/vr/elements/system_indicator.h"
 #include "chrome/browser/vr/elements/text.h"
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/elements/ui_element_debug_id.h"
+#include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/elements/ui_element_transform_operations.h"
 #include "chrome/browser/vr/elements/ui_texture.h"
 #include "chrome/browser/vr/elements/url_bar.h"
@@ -156,14 +157,53 @@
 // adjusted.
 static constexpr float kContentBoundsPropagationThreshold = 0.2f;
 
-enum DrawPhase : int {
-  kPhaseBackground = 0,
-  kPhaseFloorCeiling,
-  kPhaseForeground,
-};
-
 }  // namespace
 
+// The scene manager creates and maintains UiElements that form the following
+// hierarchy.
+//
+// kRoot
+//   k2dBrowsingRoot
+//     k2dBrowsingBackground
+//       kBackgroundLeft
+//       kBackgroundRight
+//       kBackgroundTop
+//       kBackgroundBottom
+//       kBackgroundFront
+//       kBackgroundBack
+//       kFloor
+//       kCeiling
+//     k2dBrowsingForeground
+//       kContentQuad
+//         kBackplane
+//         kIndicatorLayout
+//           kAudioCaptureIndicator
+//           kVideoCaptureIndicator
+//           kScreenCaptureIndicator
+//           kLocationAccessIndicator
+//           kBluetoothConnectedIndicator
+//           kLoadingIndicator
+//         kExitPrompt
+//           kExitPromptBackplane
+//       kCloseButton
+//       kUrlBar
+//         kLoadingIndicator
+//         kExitButton
+//     kFullscreenToast
+//     kScreenDimmer
+//     k2dBrowsingViewportAwareRoot
+//       kExitWarning
+//   kWebVrRoot
+//     kWebVrContent
+//     kWebVrViewportAwareRoot
+//       kWebVrPresentationToast
+//       kWebVrPermanentHttpSecurityWarning,
+//       kWebVrTransientHttpSecurityWarning,
+//       kTransientUrlBar
+//
+// TODO(vollick): The above hierarchy is complex, brittle, and would be easier
+// to manage if it were specified in a declarative format.
+
 UiSceneManager::UiSceneManager(UiBrowserInterface* browser,
                                UiScene* scene,
                                ContentInputDelegate* content_input_delegate,
@@ -177,6 +217,8 @@
       started_for_autopresentation_(web_vr_autopresentation_expected),
       showing_web_vr_splash_screen_(web_vr_autopresentation_expected),
       weak_ptr_factory_(this) {
+  Create2dBrowsingSubtreeRoots();
+  CreateWebVrRoot();
   CreateBackground();
   CreateViewportAwareRoot();
   CreateContentQuad(content_input_delegate);
@@ -196,17 +238,48 @@
 
 UiSceneManager::~UiSceneManager() {}
 
+void UiSceneManager::Create2dBrowsingSubtreeRoots() {
+  auto element = base::MakeUnique<UiElement>();
+  element->set_name(k2dBrowsingRoot);
+  element->set_draw_phase(kPhaseNone);
+  element->SetVisible(false);
+  element->set_hit_testable(false);
+  scene_->AddUiElement(kRoot, std::move(element));
+
+  element = base::MakeUnique<UiElement>();
+  element->set_name(k2dBrowsingBackground);
+  element->set_draw_phase(kPhaseNone);
+  element->SetVisible(false);
+  element->set_hit_testable(false);
+  scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
+
+  element = base::MakeUnique<UiElement>();
+  element->set_name(k2dBrowsingForeground);
+  element->set_draw_phase(kPhaseNone);
+  element->SetVisible(false);
+  element->set_hit_testable(false);
+  scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
+}
+
+void UiSceneManager::CreateWebVrRoot() {
+  auto element = base::MakeUnique<UiElement>();
+  element->set_name(kWebVrRoot);
+  element->set_draw_phase(kPhaseNone);
+  element->SetVisible(false);
+  element->set_hit_testable(false);
+  scene_->AddUiElement(kRoot, std::move(element));
+}
+
 void UiSceneManager::CreateScreenDimmer() {
   std::unique_ptr<UiElement> element;
   element = base::MakeUnique<ScreenDimmer>();
-  element->set_debug_id(kScreenDimmer);
-  element->set_id(AllocateId());
+  element->set_name(kScreenDimmer);
   element->set_draw_phase(kPhaseForeground);
   element->SetVisible(false);
   element->set_hit_testable(false);
   element->set_is_overlay(true);
   screen_dimmer_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
 }
 
 void UiSceneManager::CreateSecurityWarnings() {
@@ -215,8 +288,7 @@
   // TODO(mthiesse): Programatically compute the proper texture size for these
   // textured UI elements.
   element = base::MakeUnique<PermanentSecurityWarning>(512);
-  element->set_debug_id(kWebVrPermanentHttpSecurityWarning);
-  element->set_id(AllocateId());
+  element->set_name(kWebVrPermanentHttpSecurityWarning);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kPermanentWarningWidthDMM, kPermanentWarningHeightDMM);
   element->SetTranslate(0, kWarningDistance * sin(kWarningAngleRadians),
@@ -226,16 +298,14 @@
   element->SetVisible(false);
   element->set_hit_testable(false);
   element->set_viewport_aware(true);
-  viewport_aware_root_->AddChild(element.get());
   permanent_security_warning_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(element));
 
   auto transient_warning = base::MakeUnique<TransientSecurityWarning>(
       1024, base::TimeDelta::FromSeconds(kWarningTimeoutSeconds));
   transient_security_warning_ = transient_warning.get();
   element = std::move(transient_warning);
-  element->set_debug_id(kWebVrTransientHttpSecurityWarning);
-  element->set_id(AllocateId());
+  element->set_name(kWebVrTransientHttpSecurityWarning);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kTransientWarningWidthDMM, kTransientWarningHeightDMM);
   element->SetTranslate(0, 0, -kWarningDistance);
@@ -243,12 +313,10 @@
   element->SetVisible(false);
   element->set_hit_testable(false);
   element->set_viewport_aware(true);
-  viewport_aware_root_->AddChild(element.get());
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(element));
 
   element = base::MakeUnique<ExitWarning>(1024);
-  element->set_debug_id(kExitWarning);
-  element->set_id(AllocateId());
+  element->set_name(kExitWarning);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kExitWarningWidth, kExitWarningHeight);
   element->SetTranslate(0, 0, -kExitWarningDistance);
@@ -256,9 +324,8 @@
   element->SetVisible(false);
   element->set_hit_testable(false);
   element->set_viewport_aware(true);
-  viewport_aware_root_->AddChild(element.get());
   exit_warning_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(k2dBrowsingViewportAwareRoot, std::move(element));
 }
 
 void UiSceneManager::CreateSystemIndicators() {
@@ -266,7 +333,7 @@
 
   struct Indicator {
     UiElement** element;
-    UiElementDebugId debug_id;
+    UiElementName name;
     const gfx::VectorIcon& icon;
     int resource_string;
   };
@@ -285,37 +352,32 @@
 
   std::unique_ptr<LinearLayout> indicator_layout =
       base::MakeUnique<LinearLayout>(LinearLayout::kHorizontal);
+  indicator_layout->set_name(kIndicatorLayout);
   indicator_layout->set_draw_phase(kPhaseForeground);
-  indicator_layout->set_id(AllocateId());
   indicator_layout->set_y_anchoring(YAnchoring::YTOP);
   indicator_layout->SetTranslate(0, kIndicatorVerticalOffset,
                                  kIndicatorDistanceOffset);
   indicator_layout->set_margin(kIndicatorGap);
-  main_content_->AddChild(indicator_layout.get());
+  scene_->AddUiElement(kContentQuad, std::move(indicator_layout));
 
   for (const auto& indicator : indicators) {
     element = base::MakeUnique<SystemIndicator>(
         512, kIndicatorHeight, indicator.icon, indicator.resource_string);
-    element->set_debug_id(indicator.debug_id);
-    element->set_id(AllocateId());
+    element->set_name(indicator.name);
     element->set_draw_phase(kPhaseForeground);
     element->SetVisible(false);
-    indicator_layout->AddChild(element.get());
     *(indicator.element) = element.get();
     system_indicators_.push_back(element.get());
-    scene_->AddUiElement(std::move(element));
+    scene_->AddUiElement(kIndicatorLayout, std::move(element));
   }
 
-  scene_->AddUiElement(std::move(indicator_layout));
-
   ConfigureIndicators();
 }
 
 void UiSceneManager::CreateContentQuad(ContentInputDelegate* delegate) {
   std::unique_ptr<ContentElement> main_content =
       base::MakeUnique<ContentElement>(delegate);
-  main_content->set_debug_id(kContentQuad);
-  main_content->set_id(AllocateId());
+  main_content->set_name(kContentQuad);
   main_content->set_draw_phase(kPhaseForeground);
   main_content->SetSize(kContentWidth, kContentHeight);
   main_content->SetTranslate(0, kContentVerticalOffset, -kContentDistance);
@@ -325,19 +387,17 @@
       {TRANSFORM, BOUNDS});
   main_content_ = main_content.get();
   content_elements_.push_back(main_content.get());
-  scene_->AddUiElement(std::move(main_content));
+  scene_->AddUiElement(k2dBrowsingForeground, std::move(main_content));
 
   // Place an invisible but hittable plane behind the content quad, to keep the
   // reticle roughly planar with the content if near content.
   std::unique_ptr<UiElement> hit_plane = base::MakeUnique<UiElement>();
-  hit_plane->set_debug_id(kBackplane);
-  hit_plane->set_id(AllocateId());
+  hit_plane->set_name(kBackplane);
   hit_plane->set_draw_phase(kPhaseForeground);
   hit_plane->SetSize(kBackplaneSize, kBackplaneSize);
   hit_plane->SetTranslate(0, 0, -kTextureOffset);
-  main_content_->AddChild(hit_plane.get());
   content_elements_.push_back(hit_plane.get());
-  scene_->AddUiElement(std::move(hit_plane));
+  scene_->AddUiElement(kContentQuad, std::move(hit_plane));
 
   // Limit reticle distance to a sphere based on content distance.
   scene_->set_background_distance(
@@ -353,15 +413,14 @@
         return color_scheme.splash_screen_text_color;
       }),
       IDS_VR_POWERED_BY_CHROME_MESSAGE);
-  text->set_debug_id(kSplashScreenText);
-  text->set_id(AllocateId());
+  text->set_name(kSplashScreenText);
   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(std::move(text));
+  scene_->AddUiElement(kWebVrRoot, std::move(text));
 }
 
 void UiSceneManager::CreateUnderDevelopmentNotice() {
@@ -371,8 +430,7 @@
         return color_scheme.world_background_text;
       }),
       IDS_VR_UNDER_DEVELOPMENT_NOTICE);
-  text->set_debug_id(kUnderDevelopmentNotice);
-  text->set_id(AllocateId());
+  text->set_name(kUnderDevelopmentNotice);
   text->set_draw_phase(kPhaseForeground);
   text->set_hit_testable(false);
   text->SetSize(kUnderDevelopmentNoticeWidthM, kUnderDevelopmentNoticeHeightM);
@@ -380,16 +438,15 @@
   text->SetRotate(1, 0, 0, kUnderDevelopmentNoticeRotationRad);
   text->SetEnabled(true);
   text->set_y_anchoring(YAnchoring::YBOTTOM);
-  url_bar_->AddChild(text.get());
   control_elements_.push_back(text.get());
-  scene_->AddUiElement(std::move(text));
+  scene_->AddUiElement(kUrlBar, std::move(text));
 }
 
 void UiSceneManager::CreateBackground() {
 
   // Background solid-color panels.
   struct Panel {
-    UiElementDebugId debug_id;
+    UiElementName name;
     int x_offset;
     int y_offset;
     int z_offset;
@@ -407,8 +464,7 @@
   };
   for (auto& panel : panels) {
     auto panel_element = base::MakeUnique<Rect>();
-    panel_element->set_debug_id(panel.debug_id);
-    panel_element->set_id(AllocateId());
+    panel_element->set_name(panel.name);
     panel_element->set_draw_phase(kPhaseBackground);
     panel_element->SetSize(kSceneSize, kSceneSize);
     panel_element->SetTranslate(panel.x_offset * kSceneSize / 2,
@@ -418,41 +474,43 @@
                              M_PI_2 * panel.angle);
     panel_element->set_hit_testable(false);
     background_panels_.push_back(panel_element.get());
-    scene_->AddUiElement(std::move(panel_element));
+    scene_->AddUiElement(k2dBrowsingBackground, std::move(panel_element));
   }
 
   // Floor.
   auto floor = base::MakeUnique<Grid>();
-  floor->set_debug_id(kFloor);
-  floor->set_id(AllocateId());
+  floor->set_name(kFloor);
   floor->set_draw_phase(kPhaseFloorCeiling);
   floor->SetSize(kSceneSize, kSceneSize);
   floor->SetTranslate(0.0, -kSceneHeight / 2, 0.0);
   floor->SetRotate(1, 0, 0, -M_PI_2);
   floor->set_gridline_count(kFloorGridlineCount);
   floor_ = floor.get();
-  scene_->AddUiElement(std::move(floor));
+  scene_->AddUiElement(k2dBrowsingBackground, std::move(floor));
 
   // Ceiling.
   auto ceiling = base::MakeUnique<Rect>();
-  ceiling->set_debug_id(kCeiling);
-  ceiling->set_id(AllocateId());
+  ceiling->set_name(kCeiling);
   ceiling->set_draw_phase(kPhaseFloorCeiling);
   ceiling->SetSize(kSceneSize, kSceneSize);
   ceiling->SetTranslate(0.0, kSceneHeight / 2, 0.0);
   ceiling->SetRotate(1, 0, 0, M_PI_2);
   ceiling_ = ceiling.get();
-  scene_->AddUiElement(std::move(ceiling));
+  scene_->AddUiElement(k2dBrowsingBackground, std::move(ceiling));
 
   scene_->set_first_foreground_draw_phase(kPhaseForeground);
 }
 
 void UiSceneManager::CreateViewportAwareRoot() {
   auto element = base::MakeUnique<ViewportAwareRoot>();
-  element->set_id(AllocateId());
+  element->set_name(kWebVrViewportAwareRoot);
   element->set_draw_phase(kPhaseForeground);
-  viewport_aware_root_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kWebVrRoot, std::move(element));
+
+  element = base::MakeUnique<ViewportAwareRoot>();
+  element->set_name(k2dBrowsingViewportAwareRoot);
+  element->set_draw_phase(kPhaseForeground);
+  scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
 }
 
 void UiSceneManager::CreateUrlBar() {
@@ -463,39 +521,34 @@
       base::Bind(&UiSceneManager::OnSecurityIconClicked,
                  base::Unretained(this)),
       base::Bind(&UiSceneManager::OnUnsupportedMode, base::Unretained(this)));
-  url_bar->set_debug_id(kUrlBar);
-  url_bar->set_id(AllocateId());
+  url_bar->set_name(kUrlBar);
   url_bar->set_draw_phase(kPhaseForeground);
   url_bar->SetTranslate(0, kUrlBarVerticalOffset, -kUrlBarDistance);
   url_bar->SetRotate(1, 0, 0, kUrlBarRotationRad);
   url_bar->SetSize(kUrlBarWidth, kUrlBarHeight);
   url_bar_ = url_bar.get();
   control_elements_.push_back(url_bar.get());
-  scene_->AddUiElement(std::move(url_bar));
+  scene_->AddUiElement(k2dBrowsingForeground, std::move(url_bar));
 
   auto indicator = base::MakeUnique<LoadingIndicator>(256);
-  indicator->set_debug_id(kLoadingIndicator);
-  indicator->set_id(AllocateId());
+  indicator->set_name(kLoadingIndicator);
   indicator->set_draw_phase(kPhaseForeground);
   indicator->SetTranslate(0, kLoadingIndicatorVerticalOffset,
                           kLoadingIndicatorDepthOffset);
   indicator->SetSize(kLoadingIndicatorWidth, kLoadingIndicatorHeight);
-  url_bar_->AddChild(indicator.get());
   indicator->set_y_anchoring(YAnchoring::YTOP);
   loading_indicator_ = indicator.get();
   control_elements_.push_back(indicator.get());
-  scene_->AddUiElement(std::move(indicator));
+  scene_->AddUiElement(kUrlBar, std::move(indicator));
 }
 
 void UiSceneManager::CreateWebVrUrlToast() {
   auto url_bar = base::MakeUnique<WebVrUrlToast>(
       512, base::TimeDelta::FromSeconds(kWebVrUrlToastTimeoutSeconds),
       base::Bind(&UiSceneManager::OnUnsupportedMode, base::Unretained(this)));
-  url_bar->set_debug_id(kWebVrUrlToast);
-  url_bar->set_id(AllocateId());
+  url_bar->set_name(kWebVrUrlToast);
   url_bar->set_draw_phase(kPhaseForeground);
   url_bar->set_viewport_aware(true);
-  viewport_aware_root_->AddChild(url_bar.get());
   url_bar->SetVisible(false);
   url_bar->set_hit_testable(false);
   url_bar->SetTranslate(0, kWebVrUrlToastVerticalOffset,
@@ -503,21 +556,20 @@
   url_bar->SetRotate(1, 0, 0, kUrlBarRotationRad);
   url_bar->SetSize(kWebVrUrlToastWidth, kWebVrUrlToastHeight);
   webvr_url_toast_ = url_bar.get();
-  scene_->AddUiElement(std::move(url_bar));
+  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(url_bar));
 }
 
 void UiSceneManager::CreateCloseButton() {
   std::unique_ptr<Button> element = base::MakeUnique<Button>(
       base::Bind(&UiSceneManager::OnCloseButtonClicked, base::Unretained(this)),
       base::MakeUnique<CloseButtonTexture>());
-  element->set_debug_id(kCloseButton);
-  element->set_id(AllocateId());
+  element->set_name(kCloseButton);
   element->set_draw_phase(kPhaseForeground);
   element->SetTranslate(0, kContentVerticalOffset - (kContentHeight / 2) - 0.3,
                         -kCloseButtonDistance);
   element->SetSize(kCloseButtonWidth, kCloseButtonHeight);
   close_button_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(k2dBrowsingForeground, std::move(element));
 }
 
 void UiSceneManager::CreateExitPrompt() {
@@ -531,14 +583,12 @@
                  true));
   exit_prompt_ = exit_prompt.get();
   element = std::move(exit_prompt);
-  element->set_debug_id(kExitPrompt);
-  element->set_id(AllocateId());
+  element->set_name(kExitPrompt);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kExitPromptWidth, kExitPromptHeight);
   element->SetTranslate(0.0, kExitPromptVerticalOffset, kTextureOffset);
   element->SetVisible(false);
-  main_content_->AddChild(element.get());
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kContentQuad, std::move(element));
 
   // Place an invisible but hittable plane behind the exit prompt, to keep the
   // reticle roughly planar with the content if near content.
@@ -546,22 +596,19 @@
       &UiSceneManager::OnExitPromptBackplaneClicked, base::Unretained(this)));
   exit_prompt_backplane_ = backplane.get();
   element = std::move(backplane);
-  element->set_debug_id(kExitPromptBackplane);
-  element->set_id(AllocateId());
+  element->set_name(kExitPromptBackplane);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kExitPromptBackplaneSize, kExitPromptBackplaneSize);
   element->SetTranslate(0.0, 0.0, -kTextureOffset);
-  exit_prompt_->AddChild(element.get());
   exit_prompt_backplane_ = element.get();
   content_elements_.push_back(element.get());
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kExitPrompt, std::move(element));
 }
 
 void UiSceneManager::CreateToasts() {
   auto element = base::MakeUnique<ExclusiveScreenToast>(
       512, base::TimeDelta::FromSeconds(kToastTimeoutSeconds));
-  element->set_debug_id(kExclusiveScreenToast);
-  element->set_id(AllocateId());
+  element->set_name(kExclusiveScreenToast);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kToastWidthDMM, kToastHeightDMM);
   element->SetTranslate(
@@ -573,12 +620,11 @@
   element->SetVisible(false);
   element->set_hit_testable(false);
   exclusive_screen_toast_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(k2dBrowsingForeground, std::move(element));
 
   element = base::MakeUnique<ExclusiveScreenToast>(
       512, base::TimeDelta::FromSeconds(kToastTimeoutSeconds));
-  element->set_debug_id(kExclusiveScreenToastViewportAware);
-  element->set_id(AllocateId());
+  element->set_name(kExclusiveScreenToastViewportAware);
   element->set_draw_phase(kPhaseForeground);
   element->SetSize(kToastWidthDMM, kToastHeightDMM);
   element->SetTranslate(0, kWebVrToastDistance * sin(kWebVrAngleRadians),
@@ -588,9 +634,8 @@
   element->SetVisible(false);
   element->set_hit_testable(false);
   element->set_viewport_aware(true);
-  viewport_aware_root_->AddChild(element.get());
   exclusive_screen_toast_viewport_aware_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(element));
 }
 
 base::WeakPtr<UiSceneManager> UiSceneManager::GetWeakPtr() {
@@ -739,8 +784,7 @@
       main_content_->LocalTransform().matrix().get(2, 3) *
       -kBackgroundDistanceMultiplier);
 
-  for (auto& element : scene_->GetUiElements())
-    element->SetMode(mode());
+  scene_->root_element().SetMode(mode());
 
   webvr_url_toast_->SetEnabled(started_for_autopresentation_ &&
                                !showing_web_vr_splash_screen_);
@@ -942,10 +986,6 @@
   browser_->OnUnsupportedMode(mode);
 }
 
-int UiSceneManager::AllocateId() {
-  return next_available_id_++;
-}
-
 ColorScheme::Mode UiSceneManager::mode() const {
   if (incognito_)
     return ColorScheme::kModeIncognito;
diff --git a/chrome/browser/vr/ui_scene_manager.h b/chrome/browser/vr/ui_scene_manager.h
index 28781f8..766ff5a 100644
--- a/chrome/browser/vr/ui_scene_manager.h
+++ b/chrome/browser/vr/ui_scene_manager.h
@@ -72,6 +72,8 @@
   void OnExitPromptChoiceForTesting(bool chose_exit);
 
  private:
+  void Create2dBrowsingSubtreeRoots();
+  void CreateWebVrRoot();
   void CreateScreenDimmer();
   void CreateSecurityWarnings();
   void CreateSystemIndicators();
@@ -97,7 +99,6 @@
   void OnExitPromptBackplaneClicked();
   void OnCloseButtonClicked();
   void OnUnsupportedMode(UiUnsupportedMode mode);
-  int AllocateId();
   ColorScheme::Mode mode() const;
   const ColorScheme& color_scheme() const;
 
@@ -119,7 +120,6 @@
   UiElement* screen_capture_indicator_ = nullptr;
   UiElement* location_access_indicator_ = nullptr;
   UiElement* screen_dimmer_ = nullptr;
-  UiElement* viewport_aware_root_ = nullptr;
   Rect* ceiling_ = nullptr;
   Grid* floor_ = nullptr;
   UiElement* close_button_ = nullptr;
@@ -151,8 +151,6 @@
   bool bluetooth_connected_ = false;
   UiUnsupportedMode exit_vr_prompt_reason_ = UiUnsupportedMode::kCount;
 
-  int next_available_id_ = 1;
-
   std::vector<Rect*> background_panels_;
   std::vector<UiElement*> content_elements_;
   std::vector<UiElement*> control_elements_;
diff --git a/chrome/browser/vr/ui_scene_manager_unittest.cc b/chrome/browser/vr/ui_scene_manager_unittest.cc
index 05c9bd9f..9f9677d 100644
--- a/chrome/browser/vr/ui_scene_manager_unittest.cc
+++ b/chrome/browser/vr/ui_scene_manager_unittest.cc
@@ -10,7 +10,7 @@
 #include "cc/base/math_util.h"
 #include "chrome/browser/vr/color_scheme.h"
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/elements/ui_element_debug_id.h"
+#include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/target_property.h"
 #include "chrome/browser/vr/test/animation_utils.h"
 #include "chrome/browser/vr/test/constants.h"
@@ -28,18 +28,18 @@
 using TargetProperty::OPACITY;
 
 namespace {
-std::set<UiElementDebugId> kBackgroundElements = {
+std::set<UiElementName> kBackgroundElements = {
     kBackgroundFront, kBackgroundLeft, kBackgroundBack,
     kBackgroundRight, kBackgroundTop,  kBackgroundBottom};
-std::set<UiElementDebugId> kFloorCeilingBackgroundElements = {
+std::set<UiElementName> kFloorCeilingBackgroundElements = {
     kBackgroundFront, kBackgroundLeft,   kBackgroundBack, kBackgroundRight,
     kBackgroundTop,   kBackgroundBottom, kCeiling,        kFloor};
-std::set<UiElementDebugId> kElementsVisibleInBrowsing = {
+std::set<UiElementName> kElementsVisibleInBrowsing = {
     kBackgroundFront, kBackgroundLeft, kBackgroundBack,
     kBackgroundRight, kBackgroundTop,  kBackgroundBottom,
     kCeiling,         kFloor,          kContentQuad,
     kBackplane,       kUrlBar,         kUnderDevelopmentNotice};
-std::set<UiElementDebugId> kElementsVisibleWithExitPrompt = {
+std::set<UiElementName> kElementsVisibleWithExitPrompt = {
     kBackgroundFront, kBackgroundLeft,     kBackgroundBack, kBackgroundRight,
     kBackgroundTop,   kBackgroundBottom,   kCeiling,        kFloor,
     kExitPrompt,      kExitPromptBackplane};
@@ -243,19 +243,19 @@
 
   manager_->OnWebVrFrameAvailable();
   VerifyElementsVisible(
-      "Autopresented", std::set<UiElementDebugId>{
+      "Autopresented", std::set<UiElementName>{
                            kWebVrPermanentHttpSecurityWarning,
                            kWebVrTransientHttpSecurityWarning, kWebVrUrlToast});
 
   // Make sure the transient elements go away.
   task_runner_->FastForwardUntilNoTasksRemain();
-  UiElement* transient_url_bar = scene_->GetUiElementByDebugId(kWebVrUrlToast);
+  UiElement* transient_url_bar = scene_->GetUiElementByName(kWebVrUrlToast);
   EXPECT_TRUE(IsAnimating(transient_url_bar, {OPACITY, VISIBILITY}));
   // Finish the transition.
   AnimateBy(MsToDelta(1000));
   EXPECT_FALSE(IsAnimating(transient_url_bar, {OPACITY, VISIBILITY}));
-  VerifyElementsVisible("End state", std::set<UiElementDebugId>{
-                                         kWebVrPermanentHttpSecurityWarning});
+  VerifyElementsVisible(
+      "End state", std::set<UiElementName>{kWebVrPermanentHttpSecurityWarning});
 }
 
 TEST_F(UiSceneManagerTest, WebVrAutopresented) {
@@ -276,11 +276,11 @@
   manager_->SetWebVrMode(true, false);
   manager_->OnWebVrFrameAvailable();
   VerifyElementsVisible("Autopresented",
-                        std::set<UiElementDebugId>{kWebVrUrlToast});
+                        std::set<UiElementName>{kWebVrUrlToast});
 
   // Make sure the transient URL bar times out.
   task_runner_->FastForwardUntilNoTasksRemain();
-  UiElement* transient_url_bar = scene_->GetUiElementByDebugId(kWebVrUrlToast);
+  UiElement* transient_url_bar = scene_->GetUiElementByName(kWebVrUrlToast);
   EXPECT_TRUE(IsAnimating(transient_url_bar, {OPACITY, VISIBILITY}));
   // Finish the transition.
   AnimateBy(MsToDelta(1000));
@@ -309,7 +309,7 @@
   // Hold onto the background color to make sure it changes.
   SkColor initial_background = GetBackgroundColor();
   VerifyElementsVisible("Initial", kElementsVisibleInBrowsing);
-  UiElement* content_quad = scene_->GetUiElementByDebugId(kContentQuad);
+  UiElement* content_quad = scene_->GetUiElementByName(kContentQuad);
   gfx::SizeF initial_content_size = content_quad->size();
   gfx::Transform initial_position = content_quad->LocalTransform();
 
@@ -396,8 +396,7 @@
   EXPECT_CALL(*browser_,
               OnExitVrPromptResult(UiUnsupportedMode::kUnhandledPageInfo,
                                    ExitVrPromptChoice::CHOICE_NONE));
-  scene_->GetUiElementByDebugId(kExitPromptBackplane)
-      ->OnButtonUp(gfx::PointF());
+  scene_->GetUiElementByName(kExitPromptBackplane)->OnButtonUp(gfx::PointF());
   VerifyElementsVisible("Prompt still visible", kElementsVisibleWithExitPrompt);
 }
 
@@ -466,7 +465,7 @@
   manager_->SetBluetoothConnectedIndicator(true);
 
   // All elements should be hidden.
-  VerifyElementsVisible("Elements hidden", std::set<UiElementDebugId>{});
+  VerifyElementsVisible("Elements hidden", std::set<UiElementName>{});
 }
 
 TEST_F(UiSceneManagerTest, UiUpdateTransitionToWebVR) {
@@ -482,11 +481,11 @@
   manager_->SetWebVrSecureOrigin(true);
 
   // All elements should be hidden.
-  VerifyElementsVisible("Elements hidden", std::set<UiElementDebugId>{});
+  VerifyElementsVisible("Elements hidden", std::set<UiElementName>{});
 }
 
 TEST_F(UiSceneManagerTest, CaptureIndicatorsVisibility) {
-  const std::set<UiElementDebugId> indicators = {
+  const std::set<UiElementName> indicators = {
       kAudioCaptureIndicator,       kVideoCaptureIndicator,
       kScreenCaptureIndicator,      kLocationAccessIndicator,
       kBluetoothConnectedIndicator,
@@ -558,7 +557,7 @@
   AnimateBy(MsToDelta(0));
   manager_->OnProjMatrixChanged(kProjMatrix);
 
-  UiElement* content_quad = scene_->GetUiElementByDebugId(kContentQuad);
+  UiElement* content_quad = scene_->GetUiElementByName(kContentQuad);
   gfx::SizeF content_quad_size = content_quad->size();
   content_quad_size.Scale(1.2f);
   content_quad->SetSize(content_quad_size.width(), content_quad_size.height());
diff --git a/chrome/browser/vr/ui_scene_unittest.cc b/chrome/browser/vr/ui_scene_unittest.cc
index 32df386..2987ee0 100644
--- a/chrome/browser/vr/ui_scene_unittest.cc
+++ b/chrome/browser/vr/ui_scene_unittest.cc
@@ -10,7 +10,9 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
+#include "base/test/gtest_util.h"
 #include "base/values.h"
+#include "chrome/browser/vr/elements/draw_phase.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "chrome/browser/vr/elements/ui_element_transform_operations.h"
 #include "chrome/browser/vr/elements/viewport_aware_root.h"
@@ -30,11 +32,12 @@
 
 namespace {
 
-void addElement(UiScene* scene, int id) {
-  auto element = base::MakeUnique<UiElement>();
-  element->set_id(id);
-  element->set_draw_phase(0);
-  scene->AddUiElement(std::move(element));
+size_t NumElementsInSubtree(UiElement* element) {
+  size_t count = 1;
+  for (auto& child : element->children()) {
+    count += NumElementsInSubtree(child.get());
+  }
+  return count;
 }
 
 }  // namespace
@@ -42,26 +45,40 @@
 TEST(UiScene, AddRemoveElements) {
   UiScene scene;
 
-  EXPECT_EQ(scene.GetUiElements().size(), 0u);
-  addElement(&scene, 0);
-  EXPECT_EQ(scene.GetUiElements().size(), 1u);
-  addElement(&scene, 99);
-  EXPECT_EQ(scene.GetUiElements().size(), 2u);
+  // Always start with the root element.
+  EXPECT_EQ(NumElementsInSubtree(&scene.root_element()), 1u);
 
-  EXPECT_NE(scene.GetUiElementById(0), nullptr);
-  EXPECT_NE(scene.GetUiElementById(99), nullptr);
-  EXPECT_EQ(scene.GetUiElementById(1), nullptr);
+  auto element = base::MakeUnique<UiElement>();
+  element->set_draw_phase(kPhaseForeground);
+  UiElement* parent = element.get();
+  int parent_id = parent->id();
+  scene.AddUiElement(kRoot, std::move(element));
 
-  scene.RemoveUiElement(0);
-  EXPECT_EQ(scene.GetUiElements().size(), 1u);
-  EXPECT_EQ(scene.GetUiElementById(0), nullptr);
-  scene.RemoveUiElement(99);
-  EXPECT_EQ(scene.GetUiElements().size(), 0u);
-  EXPECT_EQ(scene.GetUiElementById(99), nullptr);
+  EXPECT_EQ(NumElementsInSubtree(&scene.root_element()), 2u);
 
-  scene.RemoveUiElement(0);
-  scene.RemoveUiElement(99);
-  EXPECT_EQ(scene.GetUiElements().size(), 0u);
+  element = base::MakeUnique<UiElement>();
+  element->set_draw_phase(kPhaseForeground);
+  UiElement* child = element.get();
+  int child_id = child->id();
+
+  parent->AddChild(std::move(element));
+
+  EXPECT_EQ(NumElementsInSubtree(&scene.root_element()), 3u);
+
+  EXPECT_NE(scene.GetUiElementById(parent_id), nullptr);
+  EXPECT_NE(scene.GetUiElementById(child_id), nullptr);
+  EXPECT_EQ(scene.GetUiElementById(-1), nullptr);
+
+  scene.RemoveUiElement(child_id);
+  EXPECT_EQ(NumElementsInSubtree(&scene.root_element()), 2u);
+  EXPECT_EQ(scene.GetUiElementById(child_id), nullptr);
+
+  scene.RemoveUiElement(parent_id);
+  EXPECT_EQ(NumElementsInSubtree(&scene.root_element()), 1u);
+  EXPECT_EQ(scene.GetUiElementById(parent_id), nullptr);
+
+  // It is an error to remove an already-deleted element.
+  EXPECT_DCHECK_DEATH(scene.RemoveUiElement(child_id));
 }
 
 // This test creates a parent and child UI element, each with their own
@@ -73,7 +90,7 @@
   // Add a parent element, with distinct transformations.
   // Size of the parent should be ignored by the child.
   auto element = base::MakeUnique<UiElement>();
-  element->set_id(0);
+  UiElement* parent = element.get();
   element->SetSize(1000, 1000);
 
   UiElementTransformOperations operations;
@@ -82,20 +99,18 @@
   operations.SetScale(3, 3, 1);
   element->SetTransformOperations(operations);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  scene.AddUiElement(kRoot, std::move(element));
 
   // Add a child to the parent, with different transformations.
   element = base::MakeUnique<UiElement>();
-  element->set_id(1);
-  scene.GetUiElementById(0)->AddChild(element.get());
   UiElementTransformOperations child_operations;
   child_operations.SetTranslate(3, 0, 0);
   child_operations.SetRotate(0, 0, 1, 90);
   child_operations.SetScale(2, 2, 1);
   element->SetTransformOperations(child_operations);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
-  const UiElement* child = scene.GetUiElementById(1);
+  UiElement* child = element.get();
+  parent->AddChild(std::move(element));
 
   gfx::Point3F origin(0, 0, 0);
   gfx::Point3F point(1, 0, 0);
@@ -111,48 +126,45 @@
   UiScene scene;
 
   auto element = base::MakeUnique<UiElement>();
-  element->set_id(0);
+  UiElement* parent = element.get();
   element->SetOpacity(0.5);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  scene.AddUiElement(kRoot, std::move(element));
 
   element = base::MakeUnique<UiElement>();
-  element->set_id(1);
-  scene.GetUiElementById(0)->AddChild(element.get());
+  UiElement* child = element.get();
   element->SetOpacity(0.5);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  parent->AddChild(std::move(element));
 
   scene.OnBeginFrame(MicrosecondsToTicks(0), gfx::Vector3dF());
-  EXPECT_EQ(0.5f, scene.GetUiElementById(0)->computed_opacity());
-  EXPECT_EQ(0.25f, scene.GetUiElementById(1)->computed_opacity());
+  EXPECT_EQ(0.5f, parent->computed_opacity());
+  EXPECT_EQ(0.25f, child->computed_opacity());
 }
 
 TEST(UiScene, ViewportAware) {
   UiScene scene;
 
   auto root = base::MakeUnique<ViewportAwareRoot>();
-  root->set_id(0);
+  UiElement* viewport_aware_root = root.get();
   root->set_draw_phase(0);
-  scene.AddUiElement(std::move(root));
+  scene.AddUiElement(kRoot, std::move(root));
 
   auto element = base::MakeUnique<UiElement>();
-  element->set_id(1);
+  UiElement* parent = element.get();
   element->set_viewport_aware(true);
   element->set_draw_phase(0);
-  scene.GetUiElementById(0)->AddChild(element.get());
-  scene.AddUiElement(std::move(element));
+  viewport_aware_root->AddChild(std::move(element));
 
   element = base::MakeUnique<UiElement>();
-  element->set_id(2);
-  scene.GetUiElementById(1)->AddChild(element.get());
+  UiElement* child = element.get();
   element->set_viewport_aware(false);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  parent->AddChild(std::move(element));
 
   scene.OnBeginFrame(MicrosecondsToTicks(0), gfx::Vector3dF());
-  EXPECT_TRUE(scene.GetUiElementById(1)->computed_viewport_aware());
-  EXPECT_TRUE(scene.GetUiElementById(2)->computed_viewport_aware());
+  EXPECT_TRUE(parent->computed_viewport_aware());
+  EXPECT_TRUE(child->computed_viewport_aware());
 }
 
 typedef struct {
@@ -169,26 +181,23 @@
 
   // Create a parent element with non-unity size and scale.
   auto element = base::MakeUnique<UiElement>();
-  element->set_id(0);
+  UiElement* parent = element.get();
   element->SetSize(2, 2);
   element->SetScale(2, 2, 1);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  scene.AddUiElement(kRoot, std::move(element));
 
   // Add a child to the parent, with anchoring.
   element = base::MakeUnique<UiElement>();
-  element->set_id(1);
-  scene.GetUiElementById(0)->AddChild(element.get());
+  UiElement* child = element.get();
   element->set_x_anchoring(GetParam().x_anchoring);
   element->set_y_anchoring(GetParam().y_anchoring);
   element->set_draw_phase(0);
-  scene.AddUiElement(std::move(element));
+  parent->AddChild(std::move(element));
 
   scene.OnBeginFrame(MicrosecondsToTicks(0), gfx::Vector3dF());
-  const UiElement* child = scene.GetUiElementById(1);
   EXPECT_NEAR(GetParam().expected_x, child->GetCenter().x(), TOLERANCE);
   EXPECT_NEAR(GetParam().expected_y, child->GetCenter().y(), TOLERANCE);
-  scene.RemoveUiElement(1);
 }
 
 const std::vector<AnchoringTestCase> anchoring_test_cases = {
diff --git a/chrome/common/stack_sampling_configuration.cc b/chrome/common/stack_sampling_configuration.cc
index b88ac1a9..e28d59d9 100644
--- a/chrome/common/stack_sampling_configuration.cc
+++ b/chrome/common/stack_sampling_configuration.cc
@@ -36,11 +36,9 @@
 #elif defined(OS_MACOSX)
   // Only run on canary for now.
   #if defined(GOOGLE_CHROME_BUILD)
-    // TODO(lgrey): Reenable for 10.13 when crbug.com/748254 is fixed.
-    return base::mac::IsAtMostOS10_12() &&
-         chrome::GetChannel() == version_info::Channel::CANARY;
+  return chrome::GetChannel() == version_info::Channel::CANARY;
   #else
-    return base::mac::IsAtMostOS10_12();
+  return true;
   #endif
 #else
   return false;
diff --git a/chrome/profiling/json_exporter.cc b/chrome/profiling/json_exporter.cc
index ee76a2bc..0c4bb9e 100644
--- a/chrome/profiling/json_exporter.cc
+++ b/chrome/profiling/json_exporter.cc
@@ -255,12 +255,15 @@
     const AllocationEventSet& event_set,
     const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
     std::ostream& out,
-    std::unique_ptr<base::DictionaryValue> metadata_dict) {
+    std::unique_ptr<base::DictionaryValue> metadata_dict,
+    size_t min_size_threshold,
+    size_t min_count_threshold) {
   out << "{ \"traceEvents\": [";
   WriteProcessName(pid, out);
   out << ",\n";
   WriteDumpsHeader(pid, out);
-  ExportMemoryMapsAndV2StackTraceToJSON(event_set, maps, out);
+  ExportMemoryMapsAndV2StackTraceToJSON(
+      event_set, maps, out, min_size_threshold, min_count_threshold);
   WriteDumpsFooter(out);
   out << "]";
 
@@ -277,7 +280,9 @@
 void ExportMemoryMapsAndV2StackTraceToJSON(
     const AllocationEventSet& event_set,
     const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
-    std::ostream& out) {
+    std::ostream& out,
+    size_t min_size_threshold,
+    size_t min_count_threshold) {
   // Start dictionary.
   out << "{\n";
 
@@ -321,14 +326,35 @@
   // We hardcode one type, "[unknown]".
   size_t type_string_id = AddOrGetString("[unknown]", &string_table);
 
-  // Find all backtraces referenced by the set. The backtrace storage will
-  // contain more stacks than we want to write out (it will refer to all
-  // processes, while we're only writing one). So do those only on demand.
+  // Aggregate allocations. Allocations of the same size and stack get grouped.
+  UniqueAllocCount alloc_counts;
+  for (const auto& alloc : event_set) {
+    UniqueAlloc unique_alloc(alloc.backtrace(), alloc.size());
+    alloc_counts[unique_alloc]++;
+  }
+
+  // Filter irrelevant allocations.
+  for (auto alloc = alloc_counts.begin(); alloc != alloc_counts.end();) {
+    size_t alloc_count = alloc->second;
+    size_t alloc_size = alloc->first.size;
+    size_t alloc_total_size = alloc_size * alloc_count;
+    if (alloc_total_size < min_size_threshold &&
+        alloc_count < min_count_threshold) {
+      alloc = alloc_counts.erase(alloc);
+    } else {
+      ++alloc;
+    }
+  }
+
+  // Find all backtraces referenced by the set and not filtered. The backtrace
+  // storage will contain more stacks than we want to write out (it will refer
+  // to all processes, while we're only writing one). So do those only on
+  // demand.
   //
   // The map maps backtrace keys to node IDs (computed below).
   std::map<const Backtrace*, size_t> backtraces;
-  for (const auto& event : event_set)
-    backtraces.emplace(event.backtrace(), 0);
+  for (const auto& alloc : alloc_counts)
+    backtraces.emplace(alloc.first.backtrace, 0);
 
   // Write each backtrace, converting the string for the stack entry to string
   // IDs. The backtrace -> node ID will be filled in at this time.
@@ -347,13 +373,6 @@
       << "}]";
   out << "},\n";  // End of maps section.
 
-  // Aggregate allocations. Allocations of the same size and stack get grouped.
-  UniqueAllocCount alloc_counts;
-  for (const auto& alloc : event_set) {
-    UniqueAlloc unique_alloc(alloc.backtrace(), alloc.size());
-    alloc_counts[unique_alloc]++;
-  }
-
   // Allocators section.
   out << "\"allocators\":{\"malloc\":{\n";
   WriteCounts(alloc_counts, out);
diff --git a/chrome/profiling/json_exporter.h b/chrome/profiling/json_exporter.h
index f518d91..328ed7bc 100644
--- a/chrome/profiling/json_exporter.h
+++ b/chrome/profiling/json_exporter.h
@@ -21,14 +21,18 @@
     const AllocationEventSet& set,
     const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
     std::ostream& out,
-    std::unique_ptr<base::DictionaryValue> metadata);
+    std::unique_ptr<base::DictionaryValue> metadata,
+    size_t min_size_threshold,
+    size_t min_count_threshold);
 
 // Creates a JSON string representing a JSON dictionary that contains memory
 // maps and v2 format stack traces.
 void ExportMemoryMapsAndV2StackTraceToJSON(
     const AllocationEventSet& set,
     const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
-    std::ostream& out);
+    std::ostream& out,
+    size_t min_size_threshold,
+    size_t min_count_threshold);
 
 }  // namespace profiling
 
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc
index 14d2e86..0b66ee0 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/chrome/profiling/json_exporter_unittest.cc
@@ -20,6 +20,11 @@
 
 namespace {
 
+const size_t kNoSizeThreshold = 0;
+const size_t kNoCountThreshold = 0;
+const size_t kSizeThreshold = 1500;
+const size_t kCountThreshold = 1000;
+
 using MemoryMap = std::vector<memory_instrumentation::mojom::VmRegionPtr>;
 
 static constexpr int kNoParent = -1;
@@ -152,7 +157,8 @@
   events.insert(AllocationEvent(Address(0x3), 20, bt1));
 
   std::ostringstream stream;
-  ExportAllocationEventSetToJSON(0x1234, events, MemoryMap(), stream, nullptr);
+  ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream, nullptr,
+                                 kNoSizeThreshold, kNoCountThreshold);
   std::string json = stream.str();
 
   // JSON should parse.
@@ -249,6 +255,92 @@
   EXPECT_EQ(id3, backtraces->GetList()[node3].GetInt());
 }
 
+TEST(ProfilingJsonExporterTest, SimpleWithFilteredAllocations) {
+  BacktraceStorage backtrace_storage;
+
+  std::vector<Address> stack1;
+  stack1.push_back(Address(0x1234));
+  const Backtrace* bt1 = backtrace_storage.Insert(std::move(stack1));
+
+  std::vector<Address> stack2;
+  stack2.push_back(Address(0x5678));
+  const Backtrace* bt2 = backtrace_storage.Insert(std::move(stack2));
+
+  std::vector<Address> stack3;
+  stack3.push_back(Address(0x9999));
+  const Backtrace* bt3 = backtrace_storage.Insert(std::move(stack3));
+
+  AllocationEventSet events;
+  events.insert(AllocationEvent(Address(0x1), 16, bt1));
+  events.insert(AllocationEvent(Address(0x2), 32, bt1));
+  events.insert(AllocationEvent(Address(0x3), 1000, bt2));
+  events.insert(AllocationEvent(Address(0x4), 1000, bt2));
+  for (size_t i = 0; i < kCountThreshold + 1; ++i)
+    events.insert(AllocationEvent(Address(0x5 + i), 1, bt3));
+
+  // Validate filtering by size and count.
+  std::ostringstream stream;
+  ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream, nullptr,
+                                 kSizeThreshold, kCountThreshold);
+  std::string json = stream.str();
+
+  // JSON should parse.
+  base::JSONReader reader(base::JSON_PARSE_RFC);
+  std::unique_ptr<base::Value> root = reader.ReadToValue(stream.str());
+  ASSERT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code())
+      << reader.GetErrorMessage();
+  ASSERT_TRUE(root);
+
+  // The trace array contains two items, a process_name one and a
+  // periodic_interval one. Find the latter.
+  const base::Value* periodic_interval = FindFirstPeriodicInterval(*root);
+  ASSERT_TRUE(periodic_interval) << "Array contains no periodic_interval";
+  const base::Value* heaps_v2 =
+      periodic_interval->FindPath({"args", "dumps", "heaps_v2"});
+  ASSERT_TRUE(heaps_v2);
+  const base::Value* nodes = heaps_v2->FindPath({"maps", "nodes"});
+  const base::Value* strings = heaps_v2->FindPath({"maps", "strings"});
+  ASSERT_TRUE(nodes);
+  ASSERT_TRUE(strings);
+
+  // Validate the strings table.
+  EXPECT_EQ(3u, strings->GetList().size());
+  int sid_unknown = GetStringFromStringTable(strings, "[unknown]");
+  int sid_1234 = GetStringFromStringTable(strings, "pc:1234");
+  int sid_5678 = GetStringFromStringTable(strings, "pc:5678");
+  int sid_9999 = GetStringFromStringTable(strings, "pc:9999");
+  EXPECT_NE(-1, sid_unknown);
+  EXPECT_EQ(-1, sid_1234);  // Must be filtered.
+  EXPECT_NE(-1, sid_5678);
+  EXPECT_NE(-1, sid_9999);
+
+  // Validate the nodes table.
+  // Nodes should be a list with 4 items.
+  //   [0] => address: 5678  parent: none
+  //   [1] => address: 9999  parent: none
+  EXPECT_EQ(2u, nodes->GetList().size());
+  int id0 = GetNodeWithNameID(nodes, sid_5678);
+  int id1 = GetNodeWithNameID(nodes, sid_9999);
+  EXPECT_NE(-1, id0);
+  EXPECT_NE(-1, id1);
+  EXPECT_TRUE(IsBacktraceInList(nodes, id0, kNoParent));
+  EXPECT_TRUE(IsBacktraceInList(nodes, id1, kNoParent));
+
+  // Counts should be a list with one item. Items with |bt1| are filtered.
+  // For |stack2|, there are two allocations of 1000 bytes. which is above the
+  // 1500 bytes threshold. For |stack3|, there are 1001 allocations of 1 bytes,
+  // which is above the 1000 allocations threshold.
+  const base::Value* backtraces =
+      heaps_v2->FindPath({"allocators", "malloc", "nodes"});
+  ASSERT_TRUE(backtraces);
+  EXPECT_EQ(2u, backtraces->GetList().size());
+
+  int node_bt2 = GetOffsetForBacktraceID(backtraces, id0);
+  int node_bt3 = GetOffsetForBacktraceID(backtraces, id1);
+  EXPECT_NE(-1, node_bt2);
+  EXPECT_NE(-1, node_bt3);
+}
+
 TEST(ProfilingJsonExporterTest, MemoryMaps) {
   AllocationEventSet events;
   std::vector<memory_instrumentation::mojom::VmRegionPtr> memory_maps =
@@ -257,7 +349,8 @@
   ASSERT_GT(memory_maps.size(), 2u);
 
   std::ostringstream stream;
-  ExportAllocationEventSetToJSON(1234, events, memory_maps, stream, nullptr);
+  ExportAllocationEventSetToJSON(1234, events, memory_maps, stream, nullptr,
+                                 kNoSizeThreshold, kNoCountThreshold);
   std::string json = stream.str();
 
   // JSON should parse.
@@ -304,7 +397,8 @@
 
   std::ostringstream stream;
   ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream,
-                                 std::move(metadata_dict));
+                                 std::move(metadata_dict), kNoSizeThreshold,
+                                 kNoCountThreshold);
   std::string json = stream.str();
 
   // JSON should parse.
diff --git a/chrome/profiling/memlog_connection_manager.cc b/chrome/profiling/memlog_connection_manager.cc
index 360f1c81..adf80a6 100644
--- a/chrome/profiling/memlog_connection_manager.cc
+++ b/chrome/profiling/memlog_connection_manager.cc
@@ -22,6 +22,13 @@
 
 namespace profiling {
 
+namespace {
+const size_t kMinSizeThreshold = 16 * 1024;
+const size_t kMinCountThreshold = 1024;
+const size_t kMinSizeThresholdForTracing = 0;
+const size_t kMinCountThresholdForTracing = 0;
+}  // namespace
+
 struct MemlogConnectionManager::Connection {
   Connection(AllocationTracker::CompleteCallback complete_cb,
              BacktraceStorage* backtrace_storage,
@@ -119,7 +126,8 @@
 
   std::ostringstream oss;
   ExportAllocationEventSetToJSON(pid, connection->tracker.live_allocs(), maps,
-                                 oss, std::move(metadata));
+                                 oss, std::move(metadata), kMinSizeThreshold,
+                                 kMinCountThreshold);
   std::string reply = oss.str();
 
   // Pass ownership of the underlying fd/HANDLE to zlib.
@@ -168,7 +176,8 @@
   Connection* connection = it->second.get();
   std::ostringstream oss;
   ExportMemoryMapsAndV2StackTraceToJSON(connection->tracker.live_allocs(), maps,
-                                        oss);
+                                        oss, kMinSizeThresholdForTracing,
+                                        kMinCountThresholdForTracing);
   std::string reply = oss.str();
 
   mojo::ScopedSharedBufferHandle buffer =
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 f78c92f..b24f3f0 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
@@ -492,7 +492,9 @@
       assertEq([
         {Scanning: false, State: 'Enabled', Type: 'Ethernet'},
         {Scanning: false, State: 'Enabled', Type: 'WiFi'},
-        {State: 'Uninitialized', Type: 'Cellular', SimPresent: true},
+        {State: 'Uninitialized', SIMPresent: true,
+         SIMLockStatus: {LockEnabled: true, LockType: '', RetriesLeft: 3},
+         Type: 'Cellular' },
         {State: 'Disabled', Type: 'WiMAX'},
       ],
                result);
diff --git a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
index 874253c..285764b 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
@@ -1897,6 +1897,11 @@
   document.body.appendChild(webview);
 }
 
+function testDeclarativeWebRequestAPISendMessageSecondWebView() {
+  var tempWebview = new WebView();
+  testDeclarativeWebRequestAPISendMessage();
+}
+
 // This test verifies that setting a <webview>'s style.display = 'block' does
 // not throw and attach error.
 function testDisplayBlock() {
@@ -3174,6 +3179,8 @@
   'testDeclarativeWebRequestAPI': testDeclarativeWebRequestAPI,
   'testDeclarativeWebRequestAPISendMessage':
       testDeclarativeWebRequestAPISendMessage,
+  'testDeclarativeWebRequestAPISendMessageSecondWebView':
+      testDeclarativeWebRequestAPISendMessageSecondWebView,
   'testDisplayBlock': testDisplayBlock,
   'testWebRequestAPI': testWebRequestAPI,
   'testWebRequestAPIErrorOccurred': testWebRequestAPIErrorOccurred,
diff --git a/chrome/test/data/webui/extensions/extension_manager_test.js b/chrome/test/data/webui/extensions/extension_manager_test.js
index f3479c2..d9f2a30 100644
--- a/chrome/test/data/webui/extensions/extension_manager_test.js
+++ b/chrome/test/data/webui/extensions/extension_manager_test.js
@@ -173,7 +173,10 @@
       manager.addItem(extension);
       manager.addItem(secondExtension);
       var data = manager.extensions[0];
-      manager.showItemDetails(extension);
+      // TODO(scottchen): maybe testing too many things in a single unit test.
+      manager.$['items-list'].fire(
+          'extension-item-show-details', {data: extension});
+      Polymer.dom.flush();
       var detailsView = manager.$['details-view'];
       expectEquals(extension.id, detailsView.data.id);
       expectEquals(oldDescription, detailsView.data.description);
diff --git a/chrome/test/data/webui/extensions/extension_navigation_helper_test.js b/chrome/test/data/webui/extensions/extension_navigation_helper_test.js
index 0e665f8..0390aca5 100644
--- a/chrome/test/data/webui/extensions/extension_navigation_helper_test.js
+++ b/chrome/test/data/webui/extensions/extension_navigation_helper_test.js
@@ -39,7 +39,9 @@
       var navigationHelper = new extensions.NavigationHelper(changePage);
 
       expectEquals('chrome://extensions/navigation_helper.html', location.href);
-      expectDeepEquals({page: Page.LIST}, navigationHelper.getCurrentPage());
+      expectDeepEquals(
+          {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS},
+          navigationHelper.getCurrentPage());
 
       var currentLength = history.length;
       navigationHelper.updateHistory({page: Page.DETAILS, extensionId: id});
@@ -55,7 +57,8 @@
           .then(() => {
             mock.verifyMock();
 
-            mock.addExpectation({page: Page.LIST});
+            mock.addExpectation(
+                {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS});
             var waitForNextPop = getOnPopState();
             history.back();
             return waitForNextPop;
@@ -68,9 +71,13 @@
     test(assert(TestNames.Conversions), function() {
       var id = 'a'.repeat(32);
       var stateUrlPairs = {
-        list: {
+        extensions: {
           url: 'chrome://extensions/',
-          state: {page: Page.LIST},
+          state: {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS},
+        },
+        apps: {
+          url: 'chrome://extensions/apps',
+          state: {page: Page.LIST, type: extensions.ShowingType.APPS},
         },
         details: {
           url: 'chrome://extensions/?id=' + id,
@@ -117,7 +124,9 @@
       var navigationHelper = new extensions.NavigationHelper(function() {});
 
       history.pushState({}, '', 'chrome://extensions/');
-      expectDeepEquals({page: Page.LIST}, navigationHelper.getCurrentPage());
+      expectDeepEquals(
+          {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS},
+          navigationHelper.getCurrentPage());
 
       var expectedLength = history.length;
 
diff --git a/chrome/test/data/webui/settings/bluetooth_page_tests.js b/chrome/test/data/webui/settings/bluetooth_page_tests.js
index 5644558..afda0af 100644
--- a/chrome/test/data/webui/settings/bluetooth_page_tests.js
+++ b/chrome/test/data/webui/settings/bluetooth_page_tests.js
@@ -2,6 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+function getFakePrefs() {
+  return {
+    ash: {
+      user: {
+        bluetooth: {
+          adapter_enabled: {
+            key: 'ash.user.bluetooth.adapter_enabled',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          }
+        }
+      }
+    }
+  };
+}
+
 suite('Bluetooth', function() {
   var bluetoothPage = null;
 
@@ -60,6 +76,7 @@
   setup(function() {
     PolymerTest.clearBody();
     bluetoothPage = document.createElement('settings-bluetooth-page');
+    bluetoothPage.prefs = getFakePrefs();
     assertTrue(!!bluetoothPage);
 
     bluetoothApi_.setDevicesForTest([]);
@@ -73,43 +90,51 @@
 
   test('MainPage', function() {
     assertFalse(bluetoothApi_.getAdapterStateForTest().powered);
-    assertFalse(bluetoothPage.bluetoothToggleState_);
+    assertFalse(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
     // Test that tapping the single settings-box div enables bluetooth.
     var div = bluetoothPage.$$('div.settings-box');
     assertTrue(!!div);
     MockInteractions.tap(div);
-    assertTrue(bluetoothPage.bluetoothToggleState_);
-    assertTrue(bluetoothApi_.getAdapterStateForTest().powered);
+    assertTrue(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
   });
 
   suite('SubPage', function() {
     var subpage;
 
     setup(function() {
-      bluetoothApi_.setEnabled(true);
-      Polymer.dom.flush();
+      assertFalse(bluetoothApi_.getAdapterStateForTest().powered);
+      assertFalse(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
       var div = bluetoothPage.$$('div.settings-box');
+
+      // First tap will turn on bluetooth.
       MockInteractions.tap(div);
+      assertTrue(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
+      bluetoothPage.adapterState_.powered = true;
+      // Second tap will open bluetooth subpage.
+      MockInteractions.tap(div);
+
+      Polymer.dom.flush();
       subpage = bluetoothPage.$$('settings-bluetooth-subpage');
       assertTrue(!!subpage);
-      assertTrue(subpage.bluetoothToggleState);
-      assertFalse(subpage.bluetoothToggleDisabled);
     });
 
     test('toggle', function() {
-      assertTrue(subpage.bluetoothToggleState);
+      assertTrue(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
 
       var enableButton = subpage.$.enableBluetooth;
       assertTrue(!!enableButton);
       assertTrue(enableButton.checked);
 
-      subpage.bluetoothToggleState = false;
+      bluetoothPage.setPrefValue('ash.user.bluetooth.adapter_enabled', false);
+
       assertFalse(enableButton.checked);
       assertFalse(bluetoothApi_.getAdapterStateForTest().powered);
-      assertFalse(bluetoothPage.bluetoothToggleState_);
+      assertFalse(bluetoothPage.prefs.ash.user.bluetooth.adapter_enabled.value);
     });
 
     test('paired device list', function() {
+      assertTrue(subpage.adapterState.powered);
+
       var pairedContainer = subpage.$.pairedContainer;
       assertTrue(!!pairedContainer);
       assertTrue(pairedContainer.hidden);
@@ -132,6 +157,8 @@
     });
 
     test('unpaired device list', function() {
+      assertTrue(subpage.adapterState.powered);
+
       var unpairedContainer = subpage.$.unpairedContainer;
       assertTrue(!!unpairedContainer);
       assertTrue(unpairedContainer.hidden);
@@ -154,6 +181,8 @@
     });
 
     test('pair device', function(done) {
+      assertTrue(subpage.adapterState.powered);
+
       bluetoothApi_.setDevicesForTest(fakeDevices_);
       Polymer.dom.flush();
       assertEquals(4, subpage.deviceList_.length);
@@ -170,6 +199,8 @@
     });
 
     test('pair dialog', function() {
+      assertTrue(subpage.adapterState.powered);
+
       bluetoothApi_.setDevicesForTest(fakeDevices_);
       Polymer.dom.flush();
       var dialog = subpage.$.deviceDialog;
diff --git a/chromecast/android/BUILD.gn b/chromecast/android/BUILD.gn
index 732f9f7..8ced54c7 100644
--- a/chromecast/android/BUILD.gn
+++ b/chromecast/android/BUILD.gn
@@ -20,8 +20,6 @@
 cast_shared_library("libcast_shell_android") {
   sources = [
     "//chromecast/app/android/cast_jni_loader.cc",
-    "cast_jni_registrar.cc",
-    "cast_jni_registrar.h",
   ]
 
   deps = [
diff --git a/chromecast/android/cast_jni_registrar.cc b/chromecast/android/cast_jni_registrar.cc
deleted file mode 100644
index 873048c..0000000
--- a/chromecast/android/cast_jni_registrar.cc
+++ /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.
-
-#include "chromecast/android/cast_jni_registrar.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_registrar.h"
-#include "base/macros.h"
-#include "chromecast/base/android/system_time_change_notifier_android.h"
-#include "chromecast/base/chromecast_config_android.h"
-#include "chromecast/chromecast_features.h"
-
-#if BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
-#include "chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h"
-#include "chromecast/media/cma/backend/android/volume_control_android.h"
-#endif
-
-namespace chromecast {
-namespace android {
-
-namespace {
-
-static base::android::RegistrationMethod kMethods[] = {
-    {"ChromecastConfigAndroid", ChromecastConfigAndroid::RegisterJni},
-    {"SystemTimeChangeNotifierAndroid",
-     SystemTimeChangeNotifierAndroid::RegisterJni},
-#if BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
-    {"AudioSinkAudioTrackImpl",
-     media::AudioSinkAndroidAudioTrackImpl::RegisterJni},
-    {"VolumeControlAndroid", media::VolumeControlAndroid::RegisterJni},
-#endif
-};
-
-}  // namespace
-
-bool RegisterJni(JNIEnv* env) {
-  return RegisterNativeMethods(env, kMethods, arraysize(kMethods));
-}
-
-}  // namespace android
-}  // namespace chromecast
diff --git a/chromecast/android/cast_jni_registrar.h b/chromecast/android/cast_jni_registrar.h
deleted file mode 100644
index b07316b..0000000
--- a/chromecast/android/cast_jni_registrar.h
+++ /dev/null
@@ -1,19 +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 CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_
-#define CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_
-
-#include <jni.h>
-
-namespace chromecast {
-namespace android {
-
-// Register all JNI bindings necessary for the Android cast shell.
-bool RegisterJni(JNIEnv* env);
-
-}  // namespace android
-}  // namespace chromecast
-
-#endif  // CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_
diff --git a/chromecast/base/android/system_time_change_notifier_android.cc b/chromecast/base/android/system_time_change_notifier_android.cc
index 2aa8bdf5..05d8e95 100644
--- a/chromecast/base/android/system_time_change_notifier_android.cc
+++ b/chromecast/base/android/system_time_change_notifier_android.cc
@@ -10,11 +10,6 @@
 
 namespace chromecast {
 
-// static
-bool SystemTimeChangeNotifierAndroid::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
 SystemTimeChangeNotifierAndroid::SystemTimeChangeNotifierAndroid() {
 }
 
diff --git a/chromecast/base/android/system_time_change_notifier_android.h b/chromecast/base/android/system_time_change_notifier_android.h
index 2f1aceb..e4e44b4 100644
--- a/chromecast/base/android/system_time_change_notifier_android.h
+++ b/chromecast/base/android/system_time_change_notifier_android.h
@@ -13,8 +13,6 @@
 
 class SystemTimeChangeNotifierAndroid : public SystemTimeChangeNotifier {
  public:
-  static bool RegisterJni(JNIEnv* env);
-
   SystemTimeChangeNotifierAndroid();
   ~SystemTimeChangeNotifierAndroid() override;
 
diff --git a/chromecast/base/chromecast_config_android.cc b/chromecast/base/chromecast_config_android.cc
index 1b36b79..9d87796 100644
--- a/chromecast/base/chromecast_config_android.cc
+++ b/chromecast/base/chromecast_config_android.cc
@@ -25,11 +25,6 @@
   return g_instance.Pointer();
 }
 
-// static
-bool ChromecastConfigAndroid::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
 ChromecastConfigAndroid::ChromecastConfigAndroid() {
 }
 
diff --git a/chromecast/base/chromecast_config_android.h b/chromecast/base/chromecast_config_android.h
index 4f2864f..03f8495 100644
--- a/chromecast/base/chromecast_config_android.h
+++ b/chromecast/base/chromecast_config_android.h
@@ -17,7 +17,6 @@
 class ChromecastConfigAndroid {
  public:
   static ChromecastConfigAndroid* GetInstance();
-  static bool RegisterJni(JNIEnv* env);
 
   // Returns whether or not the user has allowed sending usage stats and
   // crash reports.
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index ee414f9..578caab 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -151,11 +151,8 @@
       "android/cast_content_window_android.cc",
       "android/cast_content_window_android.h",
       "android/cast_metrics_helper_android.cc",
-      "android/cast_metrics_helper_android.h",
       "android/cast_web_contents_activity.cc",
       "android/cast_web_contents_activity.h",
-      "android/jni_registrar.cc",
-      "android/jni_registrar.h",
     ]
 
     deps += [
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 348b104..708752ed 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -34,11 +34,22 @@
   resource_dirs = [ "//chromecast/browser/android/apk/res" ]
 }
 
+android_library("cast_audio_manager_java") {
+  java_src_dir = "//chromecast/browser/android/apk/src"
+  java_files =
+      [ "$java_src_dir/org/chromium/chromecast/shell/CastAudioManager.java" ]
+}
+
+android_library("cast_intents_java") {
+  java_src_dir = "//chromecast/browser/android/apk/src"
+  java_files =
+      [ "$java_src_dir/org/chromium/chromecast/shell/CastIntents.java" ]
+}
+
 android_library("cast_shell_java") {
   java_src_dir = "//chromecast/browser/android/apk/src"
   java_files = [
     "$java_src_dir/org/chromium/chromecast/shell/CastApplication.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastAudioManager.java",
     "$java_src_dir/org/chromium/chromecast/shell/CastBrowserHelper.java",
     "$java_src_dir/org/chromium/chromecast/shell/CastContentWindowAndroid.java",
     "$java_src_dir/org/chromium/chromecast/shell/CastCrashHandler.java",
@@ -56,6 +67,8 @@
   srcjar_deps = [ ":cast_shell_build_config_gen" ]
 
   deps = [
+    ":cast_audio_manager_java",
+    ":cast_intents_java",
     ":cast_shell_android_resources",
     ":cast_shell_manifest",
     "//base:base_java",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastIntents.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastIntents.java
new file mode 100644
index 0000000..869064c
--- /dev/null
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastIntents.java
@@ -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.
+
+package org.chromium.chromecast.shell;
+
+/**
+ * A namespace for constants to uniquely describe certain public Intents that can be used to control
+ * the life cycle of CastWebContentsActivity.
+ */
+public class CastIntents {
+    public static final String ACTION_STOP_ACTIVITY =
+            "com.google.android.apps.castshell.intent.action.STOP_ACTIVITY";
+    public static final String ACTION_SCREEN_OFF =
+            "com.google.android.apps.castshell.intent.action.ACTION_SCREEN_OFF";
+}
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
index d911508..974395b 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -60,11 +60,6 @@
 
     private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300;
 
-    public static final String ACTION_STOP_ACTIVITY =
-            "com.google.android.apps.castshell.intent.action.STOP_ACTIVITY";
-    public static final String ACTION_SCREEN_OFF =
-            "com.google.android.apps.castshell.intent.action.ACTION_SCREEN_OFF";
-
     /*
      * Intended to be called from "onStop" to determine if this is a "legitimate" stop or not.
      * When starting CastShellActivity from the TV in sleep mode, an extra onPause/onStop will be
@@ -146,7 +141,7 @@
         windowDestroyedIntentFilter.addDataScheme(intent.getData().getScheme());
         windowDestroyedIntentFilter.addDataAuthority(intent.getData().getAuthority(), null);
         windowDestroyedIntentFilter.addDataPath(mInstanceId, PatternMatcher.PATTERN_LITERAL);
-        windowDestroyedIntentFilter.addAction(ACTION_STOP_ACTIVITY);
+        windowDestroyedIntentFilter.addAction(CastIntents.ACTION_STOP_ACTIVITY);
         LocalBroadcastManager.getInstance(this).registerReceiver(
                 mWindowDestroyedBroadcastReceiver, windowDestroyedIntentFilter);
 
@@ -163,7 +158,7 @@
         };
 
         IntentFilter screenOffIntentFilter = new IntentFilter();
-        screenOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+        screenOffIntentFilter.addAction(CastIntents.ACTION_SCREEN_OFF);
         LocalBroadcastManager.getInstance(this).registerReceiver(
                 mScreenOffBroadcastReceiver, screenOffIntentFilter);
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
index 70f8272b..48100d36 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
@@ -62,8 +62,8 @@
         public void stop(Context context) {
             if (DEBUG) Log.d(TAG, "stop");
 
-            Intent intent = new Intent(
-                    CastWebContentsActivity.ACTION_STOP_ACTIVITY, getInstanceUri(mInstanceId));
+            Intent intent =
+                    new Intent(CastIntents.ACTION_STOP_ACTIVITY, getInstanceUri(mInstanceId));
             LocalBroadcastManager.getInstance(context).sendBroadcastSync(intent);
         }
     }
@@ -201,4 +201,4 @@
                                   .build();
         return instanceUri;
     }
-}
\ No newline at end of file
+}
diff --git a/chromecast/browser/android/cast_metrics_helper_android.cc b/chromecast/browser/android/cast_metrics_helper_android.cc
index e7cc764..77680e09 100644
--- a/chromecast/browser/android/cast_metrics_helper_android.cc
+++ b/chromecast/browser/android/cast_metrics_helper_android.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/browser/android/cast_metrics_helper_android.h"
-
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "jni/CastMetricsHelper_jni.h"
 
@@ -12,11 +10,6 @@
 namespace chromecast {
 namespace shell {
 
-// static
-bool CastMetricsHelperAndroid::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
 void LogMediaPlay(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   metrics::CastMetricsHelper::GetInstance()->LogMediaPlay();
 }
diff --git a/chromecast/browser/android/cast_metrics_helper_android.h b/chromecast/browser/android/cast_metrics_helper_android.h
deleted file mode 100644
index 6665436..0000000
--- a/chromecast/browser/android/cast_metrics_helper_android.h
+++ /dev/null
@@ -1,27 +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.
-
-#ifndef CHROMECAST_BROWSER_ANDROID_CAST_METRICS_HELPER_ANDROID_H_
-#define CHROMECAST_BROWSER_ANDROID_CAST_METRICS_HELPER_ANDROID_H_
-
-#include <jni.h>
-#include <vector>
-
-#include "base/macros.h"
-
-namespace chromecast {
-namespace shell {
-class CastMetricsHelperAndroid {
- public:
-  // Registers the JNI methods for CastMetricsHelperAndroid.
-  static bool RegisterJni(JNIEnv* env);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(CastMetricsHelperAndroid);
-};
-
-}  // namespace shell
-}  // namespace chromecast
-
-#endif  // CHROMECAST_BROWSER_ANDROID_CAST_METRICS_HELPER_ANDROID_H_
diff --git a/chromecast/browser/android/cast_web_contents_activity.cc b/chromecast/browser/android/cast_web_contents_activity.cc
index 8f314e5..209cf5e 100644
--- a/chromecast/browser/android/cast_web_contents_activity.cc
+++ b/chromecast/browser/android/cast_web_contents_activity.cc
@@ -35,11 +35,6 @@
 }
 
 // static
-bool CastWebContentsActivity::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-// static
 CastWebContentsActivity* CastWebContentsActivity::Get(
     content::WebContents* web_contents) {
   DCHECK(web_contents);
diff --git a/chromecast/browser/android/cast_web_contents_activity.h b/chromecast/browser/android/cast_web_contents_activity.h
index 90404d8..93270f3 100644
--- a/chromecast/browser/android/cast_web_contents_activity.h
+++ b/chromecast/browser/android/cast_web_contents_activity.h
@@ -25,7 +25,6 @@
  public:
   ~CastWebContentsActivity() override;
 
-  static bool RegisterJni(JNIEnv* env);
   static CastWebContentsActivity* Get(content::WebContents* web_contents);
 
   base::android::ScopedJavaLocalRef<jobject> GetContentVideoViewEmbedder();
diff --git a/chromecast/browser/android/jni_registrar.cc b/chromecast/browser/android/jni_registrar.cc
deleted file mode 100644
index 1fc51d8..0000000
--- a/chromecast/browser/android/jni_registrar.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromecast/browser/android/jni_registrar.h"
-
-#include "chromecast/browser/android/cast_content_window_android.h"
-#include "chromecast/browser/android/cast_metrics_helper_android.h"
-#include "chromecast/browser/android/cast_web_contents_activity.h"
-
-namespace chromecast {
-namespace shell {
-
-bool RegisterJni(JNIEnv* env) {
-  if (!CastContentWindowAndroid::RegisterJni(env))
-    return false;
-  if (!CastMetricsHelperAndroid::RegisterJni(env))
-    return false;
-  if (!CastWebContentsActivity::RegisterJni(env))
-    return false;
-
-  return true;
-}
-
-}  // namespace shell
-}  // namespace chromecast
diff --git a/chromecast/browser/android/jni_registrar.h b/chromecast/browser/android/jni_registrar.h
deleted file mode 100644
index 9d6a80d..0000000
--- a/chromecast/browser/android/jni_registrar.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMECAST_BROWSER_ANDROID_JNI_REGISTRAR_H_
-#define CHROMECAST_BROWSER_ANDROID_JNI_REGISTRAR_H_
-
-#include <jni.h>
-
-namespace chromecast {
-namespace shell {
-
-// Register all JNI bindings necessary for the Android cast shell.
-bool RegisterJni(JNIEnv* env);
-
-}  // namespace shell
-}  // namespace chromecast
-
-#endif  // CHROMECAST_BROWSER_ANDROID_JNI_REGISTRAR_H_
diff --git a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc
index f7c51708..b8107c3 100644
--- a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc
+++ b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.cc
@@ -157,11 +157,6 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-// static
-bool AudioSinkAndroidAudioTrackImpl::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
 void AudioSinkAndroidAudioTrackImpl::CacheDirectBufferAddress(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h
index 56bdbe1..e2535a6c 100644
--- a/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h
+++ b/chromecast/media/cma/backend/android/audio_sink_android_audiotrack_impl.h
@@ -41,8 +41,6 @@
   // buffer larger than this size and feed it in in smaller chunks.
   static const int kDirectBufferSize = 512 * 1024;
 
-  static bool RegisterJni(JNIEnv* env);
-
   // Gets the Android audio session ids used for media and communication (TTS)
   // tracks.
   // Set a return value pointer to null if that id is not needed.
diff --git a/chromecast/media/cma/backend/android/volume_control_android.cc b/chromecast/media/cma/backend/android/volume_control_android.cc
index c34797d8..15ba370 100644
--- a/chromecast/media/cma/backend/android/volume_control_android.cc
+++ b/chromecast/media/cma/backend/android/volume_control_android.cc
@@ -47,71 +47,6 @@
 VolumeMap::VolumeMap() {
   // TODO(ckuiper): Load active volume table from Android.
   volume_map_.insert(volume_map_.end(), kDefaultVolumeMap,
-                     kDefaultVolumeMap + arraysize(kDefaultVolumeMap));
-}
-
-VolumeMap::~VolumeMap() {}
-
-float VolumeMap::VolumeToDbFS(float volume) {
-  if (volume <= volume_map_[0].level) {
-    return volume_map_[0].db;
-  }
-  for (size_t i = 1; i < volume_map_.size(); ++i) {
-    if (volume < volume_map_[i].level) {
-      const float x_range = volume_map_[i].level - volume_map_[i - 1].level;
-      const float y_range = volume_map_[i].db - volume_map_[i - 1].db;
-      const float x_pos = volume - volume_map_[i - 1].level;
-
-      return volume_map_[i - 1].db + x_pos * y_range / x_range;
-    }
-  }
-  return volume_map_[volume_map_.size() - 1].db;
-}
-
-float VolumeMap::DbFSToVolume(float db) {
-  if (db <= volume_map_[0].db) {
-    return volume_map_[0].level;
-  }
-  for (size_t i = 1; i < volume_map_.size(); ++i) {
-    if (db < volume_map_[i].db) {
-      const float x_range = volume_map_[i].db - volume_map_[i - 1].db;
-      const float y_range = volume_map_[i].level - volume_map_[i - 1].level;
-      const float x_pos = db - volume_map_[i - 1].db;
-
-      return volume_map_[i - 1].level + x_pos * y_range / x_range;
-    }
-  }
-  return volume_map_[volume_map_.size() - 1].level;
-}
-
-VolumeControlAndroid::VolumeControlAndroid()
-    : thread_("VolumeControl"),
-      initialize_complete_event_(
-          base::WaitableEvent::ResetPolicy::MANUAL,
-          base::WaitableEvent::InitialState::NOT_SIGNALED) {
-  DCHECK(j_volume_control_.is_null());
-  j_volume_control_.Reset(Java_VolumeControl_createVolumeControl(
-      base::android::AttachCurrentThread(), reinterpret_cast<intptr_t>(this)));
-
-  // Load volume map to check that the config file is correct.
-  g_volume_map.Get();
-
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  thread_.StartWithOptions(options);
-
-  thread_.task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&VolumeControlAndroid::InitializeOnThread,
-                                base::Unretained(this)));
-  initialize_complete_event_.Wait();
-}
-
-VolumeControlAndroid::~VolumeControlAndroid() {}
-
-// static
-bool VolumeControlAndroid::RegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
 
 void VolumeControlAndroid::AddVolumeObserver(VolumeObserver* observer) {
   base::AutoLock lock(observer_lock_);
diff --git a/chromecast/media/cma/backend/android/volume_control_android.h b/chromecast/media/cma/backend/android/volume_control_android.h
index 88ba5d44..7acf754 100644
--- a/chromecast/media/cma/backend/android/volume_control_android.h
+++ b/chromecast/media/cma/backend/android/volume_control_android.h
@@ -48,8 +48,6 @@
   VolumeControlAndroid();
   ~VolumeControlAndroid();
 
-  static bool RegisterJni(JNIEnv* env);
-
   void AddVolumeObserver(VolumeObserver* observer);
   void RemoveVolumeObserver(VolumeObserver* observer);
   float GetVolume(AudioContentType type);
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 0a18b78..7a5f53c 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -202,9 +202,6 @@
 const char kDisableNetworkPortalNotification[] =
     "disable-network-portal-notification";
 
-// Disables new channel switcher UI.
-const char kDisableNewChannelSwitcherUI[] = "disable-new-channel-switcher-ui";
-
 // Disables the new Korean IME in chrome://settings/languages.
 const char kDisableNewKoreanIme[] = "disable-new-korean-ime";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 2ea91073..b2e05ac2 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -65,7 +65,6 @@
 CHROMEOS_EXPORT extern const char kDisableMtpWriteSupport[];
 CHROMEOS_EXPORT extern const char kDisableMultiDisplayLayout[];
 CHROMEOS_EXPORT extern const char kDisableNetworkPortalNotification[];
-CHROMEOS_EXPORT extern const char kDisableNewChannelSwitcherUI[];
 CHROMEOS_EXPORT extern const char kDisableNewKoreanIme[];
 CHROMEOS_EXPORT extern const char kDisableNewZIPUnpacker[];
 CHROMEOS_EXPORT extern const char kDisableOfficeEditingComponentApp[];
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc
index d48c4b9..a1000d4 100644
--- a/chromeos/dbus/fake_shill_manager_client.cc
+++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -1105,6 +1105,9 @@
                                FakeShillDeviceClient::kSimPinRetryCount);
     }
     shill_device_property_map_[shill::kTypeCellular]
+                              [shill::kSIMPresentProperty] =
+                                  new base::Value(true);
+    shill_device_property_map_[shill::kTypeCellular]
                               [shill::kSIMLockStatusProperty] = simlock_dict;
     shill_device_property_map_[shill::kTypeCellular]
                               [shill::kTechnologyFamilyProperty] =
@@ -1115,6 +1118,8 @@
     base::Value* sim_present = new base::Value(present);
     shill_device_property_map_[shill::kTypeCellular]
                               [shill::kSIMPresentProperty] = sim_present;
+    if (!present)
+      shill_initial_state_map_[shill::kTypeCellular] = kNetworkDisabled;
     return true;
   } else if (arg0 == "tdls_busy") {
     if (!arg1.empty())
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
index d42d7c6e..3f9cf50 100644
--- a/chromeos/network/device_state.cc
+++ b/chromeos/network/device_state.cc
@@ -70,6 +70,7 @@
     // Set default values for SIM properties.
     sim_lock_type_.erase();
     sim_retries_left_ = 0;
+    sim_lock_enabled_ = false;
 
     const base::Value* out_value = nullptr;
     if (dict->GetWithoutPathExpansion(shill::kSIMLockTypeProperty,
@@ -81,6 +82,11 @@
       GetIntegerValue(shill::kSIMLockRetriesLeftProperty, *out_value,
                       &sim_retries_left_);
     }
+    if (dict->GetWithoutPathExpansion(shill::kSIMLockEnabledProperty,
+                                      &out_value)) {
+      GetBooleanValue(shill::kSIMLockEnabledProperty, *out_value,
+                      &sim_lock_enabled_);
+    }
     return true;
   } else if (key == shill::kMeidProperty) {
     return GetStringValue(key, value, &meid_);
diff --git a/chromeos/network/device_state.h b/chromeos/network/device_state.h
index 13ae1eb..6d7f31f 100644
--- a/chromeos/network/device_state.h
+++ b/chromeos/network/device_state.h
@@ -46,6 +46,7 @@
   const std::string& carrier() const { return carrier_; }
   const std::string& sim_lock_type() const { return sim_lock_type_; }
   int sim_retries_left() const { return sim_retries_left_; }
+  bool sim_lock_enabled() const { return sim_lock_enabled_; }
   const std::string& meid() const { return meid_; }
   const std::string& imei() const { return imei_; }
   const std::string& iccid() const { return iccid_; }
@@ -84,6 +85,7 @@
   std::string carrier_;
   std::string sim_lock_type_;
   int sim_retries_left_;
+  bool sim_lock_enabled_;
   bool sim_present_;
   std::string meid_;
   std::string imei_;
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 5708943..7d21140 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -47,8 +47,6 @@
     "net/arc_net_host_impl.h",
     "obb_mounter/arc_obb_mounter_bridge.cc",
     "obb_mounter/arc_obb_mounter_bridge.h",
-    "oemcrypto/arc_oemcrypto_bridge.cc",
-    "oemcrypto/arc_oemcrypto_bridge.h",
     "power/arc_power_bridge.cc",
     "power/arc_power_bridge.h",
     "storage_manager/arc_storage_manager.cc",
diff --git a/components/history/core/browser/browsing_history_service.cc b/components/history/core/browser/browsing_history_service.cc
index bd735ad..0596d86 100644
--- a/components/history/core/browser/browsing_history_service.cc
+++ b/components/history/core/browser/browsing_history_service.cc
@@ -88,9 +88,6 @@
   return entry1.time > entry2.time;
 }
 
-BrowsingHistoryService::QueryResultsInfo::QueryResultsInfo()
-    : reached_beginning(false), has_synced_results(false) {}
-
 BrowsingHistoryService::QueryResultsInfo::~QueryResultsInfo() {}
 
 BrowsingHistoryService::BrowsingHistoryService(
@@ -148,6 +145,8 @@
 
 void BrowsingHistoryService::WebHistoryTimeout() {
   has_synced_results_ = false;
+  query_results_info_.sync_timed_out = true;
+
   // TODO(dubroy): Communicate the failure to the front end.
   if (!query_task_tracker_.HasTrackedTasks())
     ReturnResultsToDriver();
@@ -162,9 +161,13 @@
   // Anything in-flight is invalid.
   query_task_tracker_.TryCancelAll();
   web_history_request_.reset();
-
   query_results_.clear();
 
+  // TODO(skym): Should |query_results_info_| be reset to default values
+  // instead?
+  // Set this to false until the results actually arrive.
+  query_results_info_.has_synced_results = false;
+
   if (local_history_) {
     local_history_->QueryHistory(
         search_text, options,
@@ -173,11 +176,10 @@
         &query_task_tracker_);
   }
 
-  WebHistoryService* web_history = driver_->GetWebHistoryService();
-
   // Set this to false until the results actually arrive.
   query_results_info_.has_synced_results = false;
 
+  WebHistoryService* web_history = driver_->GetWebHistoryService();
   if (web_history) {
     web_history_query_results_.clear();
 
@@ -395,7 +397,7 @@
   }
 
   query_results_info_.search_text = search_text;
-  query_results_info_.reached_beginning = results->reached_beginning();
+  query_results_info_.reached_beginning_of_local = results->reached_beginning();
   query_results_info_.start_time = options.begin_time;
   // TODO(skym): |end_time| doesn't seem to be used anymore, and this logic's
   // intention is very confusing. Consider removing.
@@ -517,8 +519,17 @@
       }
     }
   }
+
+  query_results_info_.sync_timed_out = false;
   has_synced_results_ = results_value != nullptr;
   query_results_info_.has_synced_results = has_synced_results_;
+
+  if (results_value) {
+    std::string continuation_token;
+    results_value->GetString("continuation_token", &continuation_token);
+    query_results_info_.reached_beginning_of_sync = continuation_token.empty();
+  }
+
   if (!query_task_tracker_.HasTrackedTasks())
     ReturnResultsToDriver();
 }
diff --git a/components/history/core/browser/browsing_history_service.h b/components/history/core/browser/browsing_history_service.h
index 961acaa..a4c778bf 100644
--- a/components/history/core/browser/browsing_history_service.h
+++ b/components/history/core/browser/browsing_history_service.h
@@ -107,17 +107,23 @@
 
   // Contains information about a completed history query.
   struct QueryResultsInfo {
-    QueryResultsInfo();
     ~QueryResultsInfo();
 
     // The query search text.
     base::string16 search_text;
 
-    // Whether the query reached the beginning of the database.
-    bool reached_beginning;
+    // Whether the query reached the beginning of the local database.
+    bool reached_beginning_of_local = false;
 
-    // Whether the last call to Web History returned synced results.
-    bool has_synced_results;
+    // Whether the last call to Web History timed out.
+    bool sync_timed_out = false;
+
+    // Whether the last call to Web History returned successfully with a message
+    // body.
+    bool has_synced_results = false;
+
+    // Whether the query reached the beginning of the synced history results.
+    bool reached_beginning_of_sync = false;
 
     // The localized query start time.
     base::Time start_time;
@@ -235,6 +241,8 @@
   ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
       sync_service_observer_;
 
+  // TODO(skym): Why is this duplicated between this field and
+  // |query_results_info_.has_synced_results|? Can we simplify?
   // Whether the last call to Web History returned synced results.
   bool has_synced_results_;
 
diff --git a/components/history/core/browser/browsing_history_service_unittest.cc b/components/history/core/browser/browsing_history_service_unittest.cc
index f834080..f1873637 100644
--- a/components/history/core/browser/browsing_history_service_unittest.cc
+++ b/components/history/core/browser/browsing_history_service_unittest.cc
@@ -241,12 +241,17 @@
     return *all_results.rbegin();
   }
 
-  void VerifyQueryResult(bool reached_beginning,
+  void VerifyQueryResult(bool reached_beginning_of_local,
                          bool has_synced_results,
+                         bool reached_beginning_of_sync,
                          const std::vector<TestResult>& expected_entries,
                          TestBrowsingHistoryDriver::QueryResult result) {
-    EXPECT_EQ(reached_beginning, result.second.reached_beginning);
+    EXPECT_EQ(reached_beginning_of_local,
+              result.second.reached_beginning_of_local);
     EXPECT_EQ(has_synced_results, result.second.has_synced_results);
+    EXPECT_FALSE(result.second.sync_timed_out);
+    EXPECT_EQ(reached_beginning_of_sync,
+              result.second.reached_beginning_of_sync);
     EXPECT_EQ(expected_entries.size(), result.first.size());
     for (size_t i = 0; i < expected_entries.size(); ++i) {
       VerifyEntry(expected_entries[i], result.first[i]);
@@ -254,14 +259,15 @@
   }
 
   void QueryAndVerifySingleQueryResult(
-      bool reached_beginning,
+      bool reached_beginning_of_local,
       bool has_synced_results,
+      bool reached_beginning_of_sync,
       const std::vector<TestResult>& expected_entries) {
     EXPECT_EQ(0U, driver()->GetQueryResults().size());
     TestBrowsingHistoryDriver::QueryResult result =
         QueryHistory(QueryOptions());
-    VerifyQueryResult(reached_beginning, has_synced_results, expected_entries,
-                      result);
+    VerifyQueryResult(reached_beginning_of_local, has_synced_results,
+                      reached_beginning_of_sync, expected_entries, result);
   }
 
   HistoryService* local_history() { return local_history_.get(); }
@@ -382,45 +388,52 @@
 TEST_F(BrowsingHistoryServiceTest, EmptyQueryHistoryJustLocal) {
   driver()->SetWebHistory(nullptr);
   ResetService(driver(), local_history(), nullptr);
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ true,
-                                  /*has_synced_results*/ false, {});
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ true,
+                                  /*has_synced_results*/ false,
+                                  /*reached_beginning_of_sync*/ false, {});
 }
 
 TEST_F(BrowsingHistoryServiceTest, QueryHistoryJustLocal) {
   driver()->SetWebHistory(nullptr);
   ResetService(driver(), local_history(), nullptr);
+
   AddHistory({{kUrl1, 1, kLocal}});
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ true,
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ true,
                                   /*has_synced_results*/ false,
+                                  /*reached_beginning_of_sync*/ false,
                                   {{kUrl1, 1, kLocal}});
 }
 
 TEST_F(BrowsingHistoryServiceTest, EmptyQueryHistoryJustWeb) {
   ResetService(driver(), nullptr, nullptr);
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ false,
-                                  /*has_synced_results*/ true, {});
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ false,
+                                  /*has_synced_results*/ true,
+                                  /*reached_beginning_of_sync*/ true, {});
 }
 
 TEST_F(BrowsingHistoryServiceTest, EmptyQueryHistoryDelayedWeb) {
   driver()->SetWebHistory(nullptr);
   ResetService(driver(), nullptr, sync());
   driver()->SetWebHistory(web_history());
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ false,
-                                  /*has_synced_results*/ true, {});
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ false,
+                                  /*has_synced_results*/ true,
+                                  /*reached_beginning_of_sync*/ true, {});
 }
 
 TEST_F(BrowsingHistoryServiceTest, QueryHistoryJustWeb) {
   ResetService(driver(), nullptr, sync());
   AddHistory({{kUrl1, 1, kRemote}});
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ false,
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ false,
                                   /*has_synced_results*/ true,
+                                  /*reached_beginning_of_sync*/ true,
                                   {{kUrl1, 1, kRemote}});
 }
 
 TEST_F(BrowsingHistoryServiceTest, EmptyQueryHistoryBothSources) {
   ResetService(driver(), local_history(), sync());
-  QueryAndVerifySingleQueryResult(/*reached_beginning*/ true,
-                                  /*has_synced_results*/ true, {});
+  QueryAndVerifySingleQueryResult(/*reached_beginning_of_local*/ true,
+                                  /*has_synced_results*/ true,
+                                  /*reached_beginning_of_sync*/ true, {});
 }
 
 TEST_F(BrowsingHistoryServiceTest, QueryHistoryAllSources) {
@@ -430,7 +443,8 @@
               {kUrl3, 3, kRemote},
               {kUrl1, 4, kRemote}});
   QueryAndVerifySingleQueryResult(
-      /*reached_beginning*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true,
       {{kUrl1, 4, kBoth}, {kUrl3, 3, kRemote}, {kUrl2, 2, kLocal}});
 }
 
@@ -442,12 +456,14 @@
   QueryOptions options;
   options.begin_time = OffsetToTime(2);
   options.end_time = OffsetToTime(4);
-  // Having a |reached_beginning| value of false here seems counterintuitive.
-  // Seems to be for paging by |begin_time| instead of |count|. If the local
-  // history implementation changes, feel free to update this value, all this
-  // test cares about is that BrowsingHistoryService passes the values through
-  // correctly.
-  VerifyQueryResult(/*reached_beginning*/ false, /*has_synced_results*/ true,
+  // Having a |reached_beginning_of_local| value of false here seems
+  // counterintuitive. Seems to be for paging by |begin_time| instead of
+  // |count|. If the local history implementation changes, feel free to update
+  // this value, all this test cares about is that BrowsingHistoryService passes
+  // the values through correctly.
+  VerifyQueryResult(/*reached_beginning_of_local*/ false,
+                    /*has_synced_results*/ true,
+                    /*reached_beginning_of_sync*/ true,
                     {{kUrl3, 3, kLocal}, {kUrl2, 2, kLocal}},
                     QueryHistory(options));
 }
@@ -460,9 +476,10 @@
   QueryOptions options;
   options.begin_time = OffsetToTime(2);
   options.end_time = OffsetToTime(4);
-  VerifyQueryResult(/*reached_beginning*/ true, /*has_synced_results*/ true,
-                    {{kUrl3, 3, kRemote}, {kUrl2, 2, kRemote}},
-                    QueryHistory(options));
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true,
+      {{kUrl3, 3, kRemote}, {kUrl2, 2, kRemote}}, QueryHistory(options));
 }
 
 TEST_F(BrowsingHistoryServiceTest, QueryHistoryLocalPaging) {
@@ -470,28 +487,62 @@
 
   QueryOptions options;
   options.max_count = 2;
-  VerifyQueryResult(/*reached_beginning*/ false, /*has_synced_results*/ true,
+  VerifyQueryResult(/*reached_beginning_of_local*/ false,
+                    /*has_synced_results*/ true,
+                    /*reached_beginning_of_sync*/ true,
                     {{kUrl3, 3, kLocal}, {kUrl2, 2, kLocal}},
                     QueryHistory(options));
 
   options.end_time = OffsetToTime(2);
-  VerifyQueryResult(/*reached_beginning*/ true, /*has_synced_results*/ true,
-                    {{kUrl1, 1, kLocal}}, QueryHistory(options));
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true, {{kUrl1, 1, kLocal}},
+      QueryHistory(options));
 
   options.end_time = Time();
   options.max_count = 3;
   VerifyQueryResult(
-      /*reached_beginning*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true,
       {{kUrl3, 3, kLocal}, {kUrl2, 2, kLocal}, {kUrl1, 1, kLocal}},
       QueryHistory(options));
 
   options.end_time = OffsetToTime(1);
-  VerifyQueryResult(/*reached_beginning*/ true, /*has_synced_results*/ true, {},
-                    QueryHistory(options));
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true, {}, QueryHistory(options));
 }
 
-// TODO(skym, crbug.com/756097): When the concept of reaching end of sync
-// results is added, add tests that verify this works correctly.
+TEST_F(BrowsingHistoryServiceTest, QueryHistoryRemotePaging) {
+  AddHistory({{kUrl1, 1, kRemote}, {kUrl2, 2, kRemote}, {kUrl3, 3, kRemote}});
+
+  QueryOptions options;
+  options.max_count = 2;
+  VerifyQueryResult(/*reached_beginning_of_local*/ true,
+                    /*has_synced_results*/ true,
+                    /*reached_beginning_of_sync*/ false,
+                    {{kUrl3, 3, kRemote}, {kUrl2, 2, kRemote}},
+                    QueryHistory(options));
+
+  options.end_time = OffsetToTime(2);
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true, {{kUrl1, 1, kRemote}},
+      QueryHistory(options));
+
+  options.end_time = Time();
+  options.max_count = 3;
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true,
+      {{kUrl3, 3, kRemote}, {kUrl2, 2, kRemote}, {kUrl1, 1, kRemote}},
+      QueryHistory(options));
+
+  options.end_time = OffsetToTime(1);
+  VerifyQueryResult(
+      /*reached_beginning_of_local*/ true, /*has_synced_results*/ true,
+      /*reached_beginning_of_sync*/ true, {}, QueryHistory(options));
+}
 
 // TODO(skym, crbug.com/728727): Add more test cases, particularly when
 // QueryHistory returns partial results and sequential calls are made.
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index f2e708ea..f929d01 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -54,6 +54,10 @@
 const base::Feature kThreatDomDetailsTagAndAttributeFeature{
     "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kTriggerThrottlerDailyQuotaFeature{
+    "SafeBrowsingTriggerThrottlerDailyQuota",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kV4OnlyEnabled{"SafeBrowsingV4OnlyEnabled",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -75,6 +79,7 @@
     {&kPasswordProtectionInterstitial, false},
     {&kProtectedPasswordEntryPinging, true},
     {&kThreatDomDetailsTagAndAttributeFeature, false},
+    {&kTriggerThrottlerDailyQuotaFeature, false},
     {&kV4OnlyEnabled, true},
 };
 
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
index 7d3cea8c..f4c0bbc 100644
--- a/components/safe_browsing/features.h
+++ b/components/safe_browsing/features.h
@@ -28,6 +28,7 @@
 extern const base::Feature kPasswordFieldOnFocusPinging;
 extern const base::Feature kPasswordProtectionInterstitial;
 extern const base::Feature kProtectedPasswordEntryPinging;
+
 // Specifies which non-resource HTML Elements to collect based on their tag and
 // attributes. It's a single param containing a comma-separated list of pairs.
 // For example: "tag1,id,tag1,height,tag2,foo" - this will collect elements with
@@ -35,6 +36,13 @@
 // "tag2" if they have attribute "foo" set. All tag names and attributes should
 // be lower case.
 extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
+
+// Controls the daily quota for data collection triggers. It's a single param
+// containing a comma-separated list of pairs. The format of the param is
+// "T1,Q1,T2,Q2,...Tn,Qn", where Tx is a TriggerType and Qx is how many reports
+// that trigger is allowed to send per day.
+extern const base::Feature kTriggerThrottlerDailyQuotaFeature;
+
 extern const base::Feature kV4OnlyEnabled;
 
 base::ListValue GetFeatureStatusList();
diff --git a/components/safe_browsing/triggers/BUILD.gn b/components/safe_browsing/triggers/BUILD.gn
index be02b24c9..c591384e 100644
--- a/components/safe_browsing/triggers/BUILD.gn
+++ b/components/safe_browsing/triggers/BUILD.gn
@@ -31,6 +31,7 @@
   ]
   deps = [
     "//base:base",
+    "//components/safe_browsing:features",
   ]
 }
 
diff --git a/components/safe_browsing/triggers/trigger_throttler.cc b/components/safe_browsing/triggers/trigger_throttler.cc
index ba2a6ca6..bc55854 100644
--- a/components/safe_browsing/triggers/trigger_throttler.cc
+++ b/components/safe_browsing/triggers/trigger_throttler.cc
@@ -4,36 +4,114 @@
 
 #include "components/safe_browsing/triggers/trigger_throttler.h"
 
+#include "base/metrics/field_trial_params.h"
 #include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/time/default_clock.h"
 #include "base/time/time.h"
+#include "components/safe_browsing/features.h"
 
 namespace safe_browsing {
+const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
 
 namespace {
 const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
 constexpr base::TimeDelta kOneDayTimeDelta = base::TimeDelta::FromDays(1);
 
-size_t GetDailyQuotaForTrigger(const TriggerType trigger_type) {
+// Predicate used to search |trigger_type_and_quota_list_| by trigger type.
+class TriggerTypeIs {
+ public:
+  explicit TriggerTypeIs(const TriggerType type) : type_(type) {}
+  bool operator()(const TriggerTypeAndQuotaItem& trigger_type_and_quota) {
+    return type_ == trigger_type_and_quota.first;
+  }
+
+ private:
+  TriggerType type_;
+};
+
+void ParseTriggerTypeAndQuotaParam(
+    std::vector<TriggerTypeAndQuotaItem>* trigger_type_and_quota_list) {
+  DCHECK(trigger_type_and_quota_list);
+  // If the feature is disabled we just use the default list. Otherwise the list
+  // from the Finch param will be the one used.
+  if (!base::FeatureList::IsEnabled(kTriggerThrottlerDailyQuotaFeature)) {
+    return;
+  }
+
+  trigger_type_and_quota_list->clear();
+  const std::string& trigger_and_quota_csv_param =
+      base::GetFieldTrialParamValueByFeature(kTriggerThrottlerDailyQuotaFeature,
+                                             kTriggerTypeAndQuotaParam);
+  if (trigger_and_quota_csv_param.empty()) {
+    return;
+  }
+
+  std::vector<std::string> split =
+      base::SplitString(trigger_and_quota_csv_param, ",", base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+  // If we don't have the right number of pairs in the csv then don't bother
+  // parsing further.
+  if (split.size() % 2 != 0) {
+    return;
+  }
+  for (size_t i = 0; i < split.size(); i += 2) {
+    // Make sure both the trigger type and quota are integers. Skip them if not.
+    int trigger_type_int = -1;
+    int quota_int = -1;
+    if (!base::StringToInt(split[i], &trigger_type_int) ||
+        !base::StringToInt(split[i + 1], &quota_int)) {
+      continue;
+    }
+    trigger_type_and_quota_list->push_back(
+        std::make_pair(static_cast<TriggerType>(trigger_type_int), quota_int));
+  }
+
+  std::sort(trigger_type_and_quota_list->begin(),
+            trigger_type_and_quota_list->end(),
+            [](const TriggerTypeAndQuotaItem& a,
+               const TriggerTypeAndQuotaItem& b) { return a.first < b.first; });
+}
+
+size_t GetDailyQuotaForTrigger(
+    const TriggerType trigger_type,
+    const std::vector<TriggerTypeAndQuotaItem>& trigger_quota_list) {
   switch (trigger_type) {
     case TriggerType::SECURITY_INTERSTITIAL:
       return kUnlimitedTriggerQuota;
     case TriggerType::AD_SAMPLE:
-      // TODO(lpz): Introduce a finch param to configure quota per-trigger-type.
-      return 0;
+      // These triggers have quota configured via Finch, lookup the value in
+      // |trigger_quota_list|.
+      const auto& trigger_quota_iter =
+          std::find_if(trigger_quota_list.begin(), trigger_quota_list.end(),
+                       TriggerTypeIs(trigger_type));
+      if (trigger_quota_iter != trigger_quota_list.end())
+        return trigger_quota_iter->second;
+
+      break;
   }
-  // By default, unhandled trigger types have no quota.
+  // By default, unhandled or unconfigured trigger types have no quota.
   return 0;
 }
 
 }  // namespace
 
-TriggerThrottler::TriggerThrottler() {}
+TriggerThrottler::TriggerThrottler() : clock_(new base::DefaultClock()) {
+  ParseTriggerTypeAndQuotaParam(&trigger_type_and_quota_list_);
+}
 
 TriggerThrottler::~TriggerThrottler() {}
 
+void TriggerThrottler::SetClockForTesting(
+    std::unique_ptr<base::Clock> test_clock) {
+  clock_ = std::move(test_clock);
+}
+
 bool TriggerThrottler::TriggerCanFire(const TriggerType trigger_type) const {
   // Lookup how many times this trigger is allowed to fire each day.
-  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+  const size_t trigger_quota =
+      GetDailyQuotaForTrigger(trigger_type, trigger_type_and_quota_list_);
 
   // Some basic corner cases for triggers that always fire, or disabled
   // triggers that never fire.
@@ -52,18 +130,19 @@
   if (trigger_quota > timestamps.size())
     return true;
 
-  // Otherwise, we have more events than quota, check which day they occured on.
-  // Newest events are at the end of vector so we can simply look at the
+  // Otherwise, we have more events than quota, check which day they occurred
+  // on. Newest events are at the end of vector so we can simply look at the
   // Nth-from-last entry (where N is the quota) to see if it happened within
   // the current day or earlier.
-  base::Time min_timestamp = base::Time::Now() - kOneDayTimeDelta;
+  base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
   const size_t pos = timestamps.size() - trigger_quota + 1;
   return timestamps[pos] < min_timestamp.ToTimeT();
 }
 
 void TriggerThrottler::TriggerFired(const TriggerType trigger_type) {
   // Lookup how many times this trigger is allowed to fire each day.
-  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+  const size_t trigger_quota =
+      GetDailyQuotaForTrigger(trigger_type, trigger_type_and_quota_list_);
 
   // For triggers that always fire, don't bother tracking quota.
   if (trigger_quota == kUnlimitedTriggerQuota)
@@ -71,7 +150,7 @@
 
   // Otherwise, record that the trigger fired.
   std::vector<time_t>* timestamps = &trigger_events_[trigger_type];
-  timestamps->push_back(base::Time::Now().ToTimeT());
+  timestamps->push_back(clock_->Now().ToTimeT());
 
   // Clean up the trigger events map.
   CleanupOldEvents();
@@ -80,7 +159,8 @@
 void TriggerThrottler::CleanupOldEvents() {
   for (const auto& map_iter : trigger_events_) {
     const TriggerType trigger_type = map_iter.first;
-    const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+    const size_t trigger_quota =
+        GetDailyQuotaForTrigger(trigger_type, trigger_type_and_quota_list_);
     const std::vector<time_t>& trigger_times = map_iter.second;
 
     // Skip the cleanup if we have quota room, quotas should generally be small.
@@ -88,7 +168,7 @@
       return;
 
     std::vector<time_t> tmp_trigger_times;
-    base::Time min_timestamp = base::Time::Now() - kOneDayTimeDelta;
+    base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
     // Go over the event times for this trigger and keep timestamps which are
     // newer than |min_timestamp|. We put timestamps in a temp vector that will
     // get swapped into the map in place of the existing vector.
diff --git a/components/safe_browsing/triggers/trigger_throttler.h b/components/safe_browsing/triggers/trigger_throttler.h
index 405a66b4..f4746d7 100644
--- a/components/safe_browsing/triggers/trigger_throttler.h
+++ b/components/safe_browsing/triggers/trigger_throttler.h
@@ -5,14 +5,19 @@
 #ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
 #define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
 
+#include <memory>
 #include <unordered_map>
 #include <vector>
 
 #include "base/macros.h"
-#include "base/time/time.h"
+#include "base/time/clock.h"
 
 namespace safe_browsing {
 
+// Param name of the finch param containing the comma-separated list of trigger
+// types and daily quotas.
+extern const char kTriggerTypeAndQuotaParam[];
+
 enum class TriggerType {
   SECURITY_INTERSTITIAL = 1,
   AD_SAMPLE = 2,
@@ -28,6 +33,9 @@
 using TriggerTimestampMap =
     std::unordered_map<TriggerType, std::vector<time_t>, TriggerTypeHash>;
 
+// A pair containing a TriggerType and its associated daily report quota.
+using TriggerTypeAndQuotaItem = std::pair<TriggerType, int>;
+
 // TriggerThrottler keeps track of how often each type of trigger gets fired
 // and throttles them if they fire too often.
 class TriggerThrottler {
@@ -43,14 +51,26 @@
   // should be updated.
   void TriggerFired(TriggerType trigger_type);
 
+ protected:
+  void SetClockForTesting(std::unique_ptr<base::Clock> test_clock);
+
  private:
+  friend class TriggerThrottlerTest;
+
   // Called to periodically clean-up the list of event timestamps.
   void CleanupOldEvents();
 
+  // Can be set for testing.
+  std::unique_ptr<base::Clock> clock_;
+
   // Stores each trigger type that fired along with the timestamps of when it
   // fired.
   TriggerTimestampMap trigger_events_;
 
+  // List of trigger types and their quotas, controlled by Finch feature
+  // |kTriggerThrottlerDailyQuotaFeature|.
+  std::vector<TriggerTypeAndQuotaItem> trigger_type_and_quota_list_;
+
   DISALLOW_COPY_AND_ASSIGN(TriggerThrottler);
 };
 
diff --git a/components/safe_browsing/triggers/trigger_throttler_unittest.cc b/components/safe_browsing/triggers/trigger_throttler_unittest.cc
index 1b01e72..b489bb1 100644
--- a/components/safe_browsing/triggers/trigger_throttler_unittest.cc
+++ b/components/safe_browsing/triggers/trigger_throttler_unittest.cc
@@ -4,19 +4,154 @@
 
 #include "components/safe_browsing/triggers/trigger_throttler.h"
 
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
+#include "components/safe_browsing/features.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace safe_browsing {
-namespace {
+using testing::ElementsAre;
 
-TEST(TriggerThrottler, SecurityInterstitialsHaveUnlimitedQuota) {
+namespace safe_browsing {
+
+class TriggerThrottlerTest : public ::testing::Test {
+ public:
+  void SetQuotaForTriggerType(TriggerType trigger_type, size_t max_quota) {
+    trigger_throttler_.trigger_type_and_quota_list_.push_back(
+        std::make_pair(trigger_type, max_quota));
+  }
+
+  TriggerThrottler* throttler() { return &trigger_throttler_; }
+
+  void SetTestClock(base::Clock* clock) {
+    trigger_throttler_.SetClockForTesting(base::WrapUnique(clock));
+  }
+
+  std::vector<time_t> GetEventTimestampsForTriggerType(
+      TriggerType trigger_type) {
+    return trigger_throttler_.trigger_events_[trigger_type];
+  }
+
+ private:
+  TriggerThrottler trigger_throttler_;
+};
+
+TEST_F(TriggerThrottlerTest, SecurityInterstitialsHaveUnlimitedQuota) {
   // Make sure that security interstitials never run out of quota.
-  TriggerThrottler throttler;
   for (int i = 0; i < 1000; ++i) {
-    throttler.TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
-    EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
+    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
+    EXPECT_TRUE(
+        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
   }
 }
 
-}  // namespace
+TEST_F(TriggerThrottlerTest, SecurityInterstitialQuotaCanNotBeOverwritten) {
+  // Make sure that security interstitials never run out of quota, even if we
+  // try to configure quota for this trigger type.
+  SetQuotaForTriggerType(TriggerType::SECURITY_INTERSTITIAL, 3);
+  for (int i = 0; i < 1000; ++i) {
+    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
+    EXPECT_TRUE(
+        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
+  }
+}
+
+TEST_F(TriggerThrottlerTest, TriggerExceedsQuota) {
+  // Ensure that a trigger can't fire more than its quota allows.
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
+
+  // First two triggers should work
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Third attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+}
+
+TEST_F(TriggerThrottlerTest, TriggerQuotaResetsAfterOneDay) {
+  // Ensure that trigger events older than a day are cleaned up and triggers can
+  // resume firing.
+
+  // We initialize the test clock to several days ago and fire some events to
+  // use up quota. We then advance the clock by a day and ensure quota is
+  // available again.
+  base::SimpleTestClock* test_clock(new base::SimpleTestClock);
+  test_clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(10));
+  time_t base_ts = test_clock->Now().ToTimeT();
+
+  SetTestClock(test_clock);
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
+
+  // First two triggers should work
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Third attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+
+  // Also confirm that the throttler contains two event timestamps for the above
+  // two events - since we use a test clock, it doesn't move unless we tell it
+  // to.
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(base_ts, base_ts));
+
+  // Move the clock forward by 1 day (and a bit) and try the trigger again,
+  // quota should be available now.
+  test_clock->Advance(base::TimeDelta::FromDays(1) +
+                      base::TimeDelta::FromSeconds(1));
+  time_t advanced_ts = test_clock->Now().ToTimeT();
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+
+  // The previous time stamps should remain in the throttler.
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(base_ts, base_ts));
+
+  // Firing the trigger will clean up the expired timestamps and insert the new
+  // timestamp.
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(advanced_ts));
+}
+
+TEST(TriggerThrottlerTestFinch, ConfigureQuotaViaFinch) {
+  // Make sure that setting the quota param via Finch params works as expected.
+  base::FieldTrialList field_trial_list(nullptr);
+  base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
+      safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, "Group");
+  std::map<std::string, std::string> feature_params;
+  feature_params[std::string(safe_browsing::kTriggerTypeAndQuotaParam)] =
+      base::StringPrintf("%d,%d", TriggerType::AD_SAMPLE, 3);
+  base::AssociateFieldTrialParams(
+      safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, "Group",
+      feature_params);
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+  feature_list->InitializeFromCommandLine(
+      safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, std::string());
+  feature_list->AssociateReportingFieldTrial(
+      safe_browsing::kTriggerThrottlerDailyQuotaFeature.name,
+      base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
+
+  // The throttler has been configured (above) to allow ad samples to fire three
+  // times per day.
+  TriggerThrottler throttler;
+
+  // First three triggers should work
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Fourth attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+}
 }  // namespace safe_browsing
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4d5042db..b5a2806c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -745,6 +745,8 @@
     "frame_host/frame_tree_node.h",
     "frame_host/frame_tree_node_blame_context.cc",
     "frame_host/frame_tree_node_blame_context.h",
+    "frame_host/input/input_injector_impl.cc",
+    "frame_host/input/input_injector_impl.h",
     "frame_host/input/legacy_ipc_frame_input_handler.cc",
     "frame_host/input/legacy_ipc_frame_input_handler.h",
     "frame_host/interstitial_page_impl.cc",
diff --git a/content/browser/android/dialog_overlay_impl.cc b/content/browser/android/dialog_overlay_impl.cc
index 29271e2..b6a6368b 100644
--- a/content/browser/android/dialog_overlay_impl.cc
+++ b/content/browser/android/dialog_overlay_impl.cc
@@ -73,10 +73,17 @@
                                      const JavaParamRef<jobject>& obj) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  WebContentsDelegate* delegate = web_contents()->GetDelegate();
+
+  if (!delegate) {
+    Stop();
+    return;
+  }
+
   // Note: It's ok to call SetOverlayMode() directly here, because there can be
   // at most one overlay alive at the time. This logic needs to be updated if
   // ever AndroidOverlayProviderImpl.MAX_OVERLAYS > 1.
-  web_contents()->GetDelegate()->SetOverlayMode(true);
+  delegate->SetOverlayMode(true);
 
   // Send the initial token, if there is one.  The observer will notify us about
   // changes only.
@@ -134,7 +141,9 @@
   // We clear overlay mode here rather than in Destroy(), because we may have
   // been called via a WebContentsDestroyed() event, and this might be the last
   // opportunity we have to access web_contents().
-  web_contents()->GetDelegate()->SetOverlayMode(false);
+  WebContentsDelegate* delegate = web_contents()->GetDelegate();
+  if (delegate)
+    delegate->SetOverlayMode(false);
 
   cvc_->RemoveObserver(this);
   cvc_ = nullptr;
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 26d257aa..7576665 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -78,6 +78,7 @@
     view_->SetFrameConnectorDelegate(nullptr);
   }
 
+  ResetFrameRect();
   view_ = view;
 
   // Attach ourselves to the new view and size it appropriately. Also update
@@ -88,6 +89,8 @@
     SetRect(child_frame_rect_);
     if (is_hidden_)
       OnVisibilityChanged(false);
+    frame_proxy_in_parent_renderer_->Send(new FrameMsg_ViewChanged(
+        frame_proxy_in_parent_renderer_->GetRoutingID()));
   }
 }
 
@@ -267,7 +270,9 @@
 }
 
 void CrossProcessFrameConnector::OnFrameRectChanged(
-    const gfx::Rect& frame_rect) {
+    const gfx::Rect& frame_rect,
+    const viz::LocalSurfaceId& local_surface_id) {
+  local_surface_id_ = local_surface_id;
   if (!frame_rect.size().IsEmpty())
     SetRect(frame_rect);
 }
@@ -392,4 +397,9 @@
       ->SetVisibilityForChildViews(visible);
 }
 
+void CrossProcessFrameConnector::ResetFrameRect() {
+  local_surface_id_ = viz::LocalSurfaceId();
+  child_frame_rect_ = gfx::Rect();
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/cross_process_frame_connector.h b/content/browser/frame_host/cross_process_frame_connector.h
index c14b061..43468081 100644
--- a/content/browser/frame_host/cross_process_frame_connector.h
+++ b/content/browser/frame_host/cross_process_frame_connector.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "cc/output/compositor_frame.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
 #include "content/browser/renderer_host/frame_connector_delegate.h"
 #include "content/common/content_export.h"
 
@@ -111,8 +112,13 @@
  private:
   friend class MockCrossProcessFrameConnector;
 
+  // Resets the rect and the viz::LocalSurfaceId of the connector to ensure the
+  // unguessable surface ID is not reused after a cross-process navigation.
+  void ResetFrameRect();
+
   // Handlers for messages received from the parent frame.
-  void OnFrameRectChanged(const gfx::Rect& frame_rect);
+  void OnFrameRectChanged(const gfx::Rect& frame_rect,
+                          const viz::LocalSurfaceId& local_surface_id);
   void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection);
   void OnVisibilityChanged(bool visible);
   void OnSetIsInert(bool);
diff --git a/content/browser/frame_host/input/input_injector_impl.cc b/content/browser/frame_host/input/input_injector_impl.cc
new file mode 100644
index 0000000..cf14431c
--- /dev/null
+++ b/content/browser/frame_host/input/input_injector_impl.cc
@@ -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.
+
+#include "content/browser/frame_host/input/input_injector_impl.h"
+
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace content {
+
+namespace {
+
+void SyntheticGestureCallback(base::OnceClosure callback,
+                              SyntheticGesture::Result result) {
+  std::move(callback).Run();
+}
+
+}  // namespace
+
+InputInjectorImpl::InputInjectorImpl(
+    base::WeakPtr<RenderFrameHostImpl> frame_host)
+    : frame_host_(std::move(frame_host)) {}
+
+InputInjectorImpl::~InputInjectorImpl() {}
+
+void InputInjectorImpl::Create(base::WeakPtr<RenderFrameHostImpl> frame_host,
+                               mojom::InputInjectorRequest request) {
+  mojo::MakeStrongBinding(base::MakeUnique<InputInjectorImpl>(frame_host),
+                          std::move(request));
+}
+
+void InputInjectorImpl::QueueSyntheticSmoothDrag(
+    const SyntheticSmoothDragGestureParams& drag,
+    QueueSyntheticSmoothDragCallback callback) {
+  if (!frame_host_)
+    return;
+  frame_host_->GetRenderWidgetHost()->QueueSyntheticGesture(
+      SyntheticGesture::Create(drag),
+      base::BindOnce(SyntheticGestureCallback, std::move(callback)));
+}
+
+void InputInjectorImpl::QueueSyntheticSmoothScroll(
+    const SyntheticSmoothScrollGestureParams& scroll,
+    QueueSyntheticSmoothScrollCallback callback) {
+  if (!frame_host_)
+    return;
+  frame_host_->GetRenderWidgetHost()->QueueSyntheticGesture(
+      SyntheticGesture::Create(scroll),
+      base::BindOnce(SyntheticGestureCallback, std::move(callback)));
+}
+
+void InputInjectorImpl::QueueSyntheticPinch(
+    const SyntheticPinchGestureParams& pinch,
+    QueueSyntheticPinchCallback callback) {
+  if (!frame_host_)
+    return;
+  frame_host_->GetRenderWidgetHost()->QueueSyntheticGesture(
+      SyntheticGesture::Create(pinch),
+      base::BindOnce(SyntheticGestureCallback, std::move(callback)));
+}
+
+void InputInjectorImpl::QueueSyntheticTap(const SyntheticTapGestureParams& tap,
+                                          QueueSyntheticTapCallback callback) {
+  if (!frame_host_)
+    return;
+  frame_host_->GetRenderWidgetHost()->QueueSyntheticGesture(
+      SyntheticGesture::Create(tap),
+      base::BindOnce(SyntheticGestureCallback, std::move(callback)));
+}
+
+void InputInjectorImpl::QueueSyntheticPointerAction(
+    const SyntheticPointerActionListParams& pointer_action,
+    QueueSyntheticPointerActionCallback callback) {
+  if (!frame_host_)
+    return;
+  frame_host_->GetRenderWidgetHost()->QueueSyntheticGesture(
+      SyntheticGesture::Create(pointer_action),
+      base::BindOnce(SyntheticGestureCallback, std::move(callback)));
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/input/input_injector_impl.h b/content/browser/frame_host/input/input_injector_impl.h
new file mode 100644
index 0000000..4928854
--- /dev/null
+++ b/content/browser/frame_host/input/input_injector_impl.h
@@ -0,0 +1,45 @@
+// 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_BROWSER_FRAME_HOST_INPUT_INPUT_INJECTOR_IMPL_H_
+#define CONTENT_BROWSER_FRAME_HOST_INPUT_INPUT_INJECTOR_IMPL_H_
+
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/common/input/input_injector.mojom.h"
+
+namespace content {
+
+// An implementation of InputInjector.
+class CONTENT_EXPORT InputInjectorImpl : public mojom::InputInjector {
+ public:
+  explicit InputInjectorImpl(base::WeakPtr<RenderFrameHostImpl> frame_host);
+  ~InputInjectorImpl() override;
+
+  static void Create(base::WeakPtr<RenderFrameHostImpl> frame_host,
+                     mojom::InputInjectorRequest request);
+
+  // mojom::InputInjector overrides.
+  void QueueSyntheticSmoothDrag(
+      const SyntheticSmoothDragGestureParams& drag,
+      QueueSyntheticSmoothDragCallback callback) override;
+  void QueueSyntheticSmoothScroll(
+      const SyntheticSmoothScrollGestureParams& scroll,
+      QueueSyntheticSmoothScrollCallback callback) override;
+  void QueueSyntheticPinch(const SyntheticPinchGestureParams& pinch,
+                           QueueSyntheticPinchCallback callback) override;
+  void QueueSyntheticTap(const SyntheticTapGestureParams& tap,
+                         QueueSyntheticTapCallback callback) override;
+  void QueueSyntheticPointerAction(
+      const SyntheticPointerActionListParams& pointer_action,
+      QueueSyntheticPointerActionCallback callback) override;
+
+ private:
+  base::WeakPtr<RenderFrameHostImpl> frame_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputInjectorImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FRAME_HOST_INPUT_INPUT_INJECTOR_IMPL_H_
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index d691aa7..930fd055 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -19,6 +19,7 @@
 #include "base/process/kill.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "cc/base/switches.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 #include "content/browser/bluetooth/web_bluetooth_service_impl.h"
@@ -31,6 +32,7 @@
 #include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/input/input_injector_impl.h"
 #include "content/browser/frame_host/input/legacy_ipc_frame_input_handler.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
@@ -3025,6 +3027,12 @@
 
   registry_->AddInterface(
       base::Bind(&media::WatchTimeRecorder::CreateWatchTimeRecorderProvider));
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          cc::switches::kEnableGpuBenchmarking)) {
+    registry_->AddInterface(
+        base::Bind(&InputInjectorImpl::Create, weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
 void RenderFrameHostImpl::ResetWaitingState() {
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index 63b39d28..e17b42ae1 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -124,7 +124,7 @@
     // Since we were last shown, our renderer may have had a different surface
     // set (e.g. showing an interstitial), so we resend our current surface to
     // the renderer.
-    if (local_surface_id_.is_valid())
+    if (last_received_local_surface_id_.is_valid())
       SendSurfaceInfoToEmbedder();
   }
   host_->WasShown(ui::LatencyInfo());
diff --git a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index 10c77e06..921518cc 100644
--- a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -196,9 +196,10 @@
     DCHECK(view_);
     RenderWidgetHostViewChildFrame* rwhvcf =
         static_cast<RenderWidgetHostViewChildFrame*>(view_);
-    if (!rwhvcf->local_surface_id_.is_valid())
+    if (!rwhvcf->last_received_local_surface_id_.is_valid())
       return viz::SurfaceId();
-    return viz::SurfaceId(rwhvcf->frame_sink_id_, rwhvcf->local_surface_id_);
+    return viz::SurfaceId(rwhvcf->frame_sink_id_,
+                          rwhvcf->last_received_local_surface_id_);
   }
 
  protected:
diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc
index 2d89495..a081f56 100644
--- a/content/browser/loader/resource_scheduler.cc
+++ b/content/browser/loader/resource_scheduler.cc
@@ -38,10 +38,13 @@
 
 // When kPrioritySupportedRequestsDelayable is enabled, requests for
 // H2/QUIC/SPDY resources can be delayed by the ResourceScheduler just as
-// HTTP/1.1 resources are. Disabling this appears to have negative performance
-// impact, see https://crbug.com/655585.
+// HTTP/1.1 resources are. It has good impact on performance, but breaks
+// expected behavior of H2. See intent-to-unship:
+// https://groups.google.com/a/chromium.org/forum/#!topic/blink-
+// dev/ChqGX8UyHz8. We're keeping it around for finch trials to compare
+// alternatives to.
 const base::Feature kPrioritySupportedRequestsDelayable{
-    "PrioritySupportedRequestsDelayable", base::FEATURE_ENABLED_BY_DEFAULT};
+    "PrioritySupportedRequestsDelayable", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // When enabled, low-priority H2 and QUIC requests are throttled, but only
 // when the parser is in head.
diff --git a/content/browser/renderer_host/frame_connector_delegate.h b/content/browser/renderer_host/frame_connector_delegate.h
index d35cd38..8cbaa03 100644
--- a/content/browser/renderer_host/frame_connector_delegate.h
+++ b/content/browser/renderer_host/frame_connector_delegate.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_FRAME_CONNECTOR_DELEGATE_H_
 #define CONTENT_BROWSER_RENDERER_HOST_FRAME_CONNECTOR_DELEGATE_H_
 
+#include "components/viz/common/surfaces/local_surface_id.h"
 #include "content/browser/renderer_host/event_with_latency_info.h"
 #include "content/common/content_export.h"
 #include "content/common/input/input_event_ack_state.h"
@@ -128,6 +129,12 @@
     return viewport_intersection_rect_;
   }
 
+  // Returns the viz::LocalSurfaceId propagated from the parent to be used by
+  // this child frame.
+  const viz::LocalSurfaceId& local_surface_id() const {
+    return local_surface_id_;
+  }
+
   // Determines whether the current view's content is inert, either because
   // an HTMLDialogElement is being modally displayed in a higher-level frame,
   // or because the inert attribute has been specified.
@@ -149,6 +156,8 @@
   // This is here rather than in the implementation class so that
   // ViewportIntersection() can return a reference.
   gfx::Rect viewport_intersection_rect_;
+
+  viz::LocalSurfaceId local_surface_id_;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller.cc b/content/browser/renderer_host/input/synthetic_gesture_controller.cc
index 3c5dcdc..44308e0 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_controller.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_controller.cc
@@ -28,13 +28,13 @@
 
 void SyntheticGestureController::QueueSyntheticGesture(
     std::unique_ptr<SyntheticGesture> synthetic_gesture,
-    const OnGestureCompleteCallback& completion_callback) {
+    OnGestureCompleteCallback completion_callback) {
   DCHECK(synthetic_gesture);
 
   bool was_empty = pending_gesture_queue_.IsEmpty();
 
   pending_gesture_queue_.Push(std::move(synthetic_gesture),
-                              completion_callback);
+                              std::move(completion_callback));
 
   if (was_empty)
     StartGesture(*pending_gesture_queue_.FrontGesture());
@@ -95,14 +95,14 @@
 
 void SyntheticGestureController::StopGesture(
     const SyntheticGesture& gesture,
-    const OnGestureCompleteCallback& completion_callback,
+    OnGestureCompleteCallback completion_callback,
     SyntheticGesture::Result result) {
   DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
   TRACE_EVENT_ASYNC_END0("input,benchmark",
                          "SyntheticGestureController::running",
                          &gesture);
 
-  completion_callback.Run(result);
+  std::move(completion_callback).Run(result);
 }
 
 SyntheticGestureController::GestureAndCallbackQueue::GestureAndCallbackQueue() {
diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller.h b/content/browser/renderer_host/input/synthetic_gesture_controller.h
index 5ed093a..cbb4403 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_controller.h
+++ b/content/browser/renderer_host/input/synthetic_gesture_controller.h
@@ -41,11 +41,11 @@
       std::unique_ptr<SyntheticGestureTarget> gesture_target);
   virtual ~SyntheticGestureController();
 
-  typedef base::Callback<void(SyntheticGesture::Result)>
+  typedef base::OnceCallback<void(SyntheticGesture::Result)>
       OnGestureCompleteCallback;
   void QueueSyntheticGesture(
       std::unique_ptr<SyntheticGesture> synthetic_gesture,
-      const OnGestureCompleteCallback& completion_callback);
+      OnGestureCompleteCallback completion_callback);
 
   bool DispatchNextEvent(base::TimeTicks = base::TimeTicks::Now());
 
@@ -55,7 +55,7 @@
   void StartTimer();
   void StartGesture(const SyntheticGesture& gesture);
   void StopGesture(const SyntheticGesture& gesture,
-                   const OnGestureCompleteCallback& completion_callback,
+                   OnGestureCompleteCallback completion_callback,
                    SyntheticGesture::Result result);
 
   Delegate* const delegate_;
@@ -68,9 +68,9 @@
     GestureAndCallbackQueue();
     ~GestureAndCallbackQueue();
     void Push(std::unique_ptr<SyntheticGesture> gesture,
-              const OnGestureCompleteCallback& callback) {
+              OnGestureCompleteCallback callback) {
       gestures_.push_back(std::move(gesture));
-      callbacks_.push(callback);
+      callbacks_.push(std::move(callback));
     }
     void Pop() {
       gestures_.erase(gestures_.begin());
@@ -78,8 +78,11 @@
       result_of_current_gesture_ = SyntheticGesture::GESTURE_RUNNING;
     }
     SyntheticGesture* FrontGesture() { return gestures_.front().get(); }
-    OnGestureCompleteCallback& FrontCallback() {
-      return callbacks_.front();
+    OnGestureCompleteCallback FrontCallback() {
+      // TODO(dtapuska): This is odd moving the top callback. Pop really
+      // should be rewritten to take two output parameters then we can
+      // remove FrontGesture/FrontCallback.
+      return std::move(callbacks_.front());
     }
     bool IsEmpty() const {
       CHECK(gestures_.empty() == callbacks_.empty());
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 25b4786..b72bce8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -600,8 +600,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostImpl, msg)
     IPC_MESSAGE_HANDLER(FrameHostMsg_RenderProcessGone, OnRenderProcessGone)
     IPC_MESSAGE_HANDLER(FrameHostMsg_HittestData, OnHittestData)
-    IPC_MESSAGE_HANDLER(InputHostMsg_QueueSyntheticGesture,
-                        OnQueueSyntheticGesture)
     IPC_MESSAGE_HANDLER(InputHostMsg_ImeCancelComposition,
                         OnImeCancelComposition)
     IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnClose)
@@ -1369,7 +1367,7 @@
 
 void RenderWidgetHostImpl::QueueSyntheticGesture(
     std::unique_ptr<SyntheticGesture> synthetic_gesture,
-    const base::Callback<void(SyntheticGesture::Result)>& on_complete) {
+    base::OnceCallback<void(SyntheticGesture::Result)> on_complete) {
   if (!synthetic_gesture_controller_ && view_) {
     synthetic_gesture_controller_ =
         base::MakeUnique<SyntheticGestureController>(
@@ -1377,7 +1375,7 @@
   }
   if (synthetic_gesture_controller_) {
     synthetic_gesture_controller_->QueueSyntheticGesture(
-        std::move(synthetic_gesture), on_complete);
+        std::move(synthetic_gesture), std::move(on_complete));
   }
 }
 
@@ -2074,22 +2072,6 @@
     WasResized();
 }
 
-void RenderWidgetHostImpl::OnQueueSyntheticGesture(
-    const SyntheticGesturePacket& gesture_packet) {
-  // Only allow untrustworthy gestures if explicitly enabled.
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          cc::switches::kEnableGpuBenchmarking)) {
-    bad_message::ReceivedBadMessage(GetProcess(),
-                                    bad_message::RWH_SYNTHETIC_GESTURE);
-    return;
-  }
-
-  QueueSyntheticGesture(
-        SyntheticGesture::Create(*gesture_packet.gesture_params()),
-        base::Bind(&RenderWidgetHostImpl::OnSyntheticGestureCompleted,
-                   weak_factory_.GetWeakPtr()));
-}
-
 void RenderWidgetHostImpl::OnSetCursor(const WebCursor& cursor) {
   SetCursor(cursor);
 }
@@ -2407,13 +2389,6 @@
   }
 }
 
-void RenderWidgetHostImpl::OnSyntheticGestureCompleted(
-    SyntheticGesture::Result result) {
-  // TODO(dtapuska): Define mojo interface for InputHostMsg's and this will be a
-  // callback for completing InputHostMsg_QueueSyntheticGesture.
-  process_->Send(new InputMsg_SyntheticGestureCompleted(GetRoutingID()));
-}
-
 bool RenderWidgetHostImpl::ShouldDropInputEvents() const {
   return ignore_input_events_ || process_->IgnoreInputEvents() || !delegate_;
 }
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index d737008..b75d5e7 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -41,7 +41,6 @@
 #include "content/common/drag_event_source_info.h"
 #include "content/common/input/input_event_ack_state.h"
 #include "content/common/input/input_handler.mojom.h"
-#include "content/common/input/synthetic_gesture_packet.h"
 #include "content/common/render_widget_surface_properties.h"
 #include "content/common/view_message_enums.h"
 #include "content/common/widget.mojom.h"
@@ -404,7 +403,7 @@
   // callback when the gesture is finished running.
   void QueueSyntheticGesture(
       std::unique_ptr<SyntheticGesture> synthetic_gesture,
-      const base::Callback<void(SyntheticGesture::Result)>& on_complete);
+      base::OnceCallback<void(SyntheticGesture::Result)> on_complete);
 
   void CancelUpdateTextDirection();
 
@@ -671,7 +670,6 @@
   void OnSetTooltipText(const base::string16& tooltip_text,
                         blink::WebTextDirection text_direction_hint);
   void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
-  void OnQueueSyntheticGesture(const SyntheticGesturePacket& gesture_packet);
   void OnSetCursor(const WebCursor& cursor);
   void OnAutoscrollStart(const gfx::PointF& position);
   void OnAutoscrollFling(const gfx::Vector2dF& velocity);
@@ -742,8 +740,6 @@
                          InputEventAckState ack_result) override;
   void OnUnexpectedEventAck(UnexpectedEventAckType type) override;
 
-  void OnSyntheticGestureCompleted(SyntheticGesture::Result result);
-
   // Called when there is a new auto resize (using a post to avoid a stack
   // which may get in recursive loops).
   void DelayedAutoResized();
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index b555878d..faec1e5 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -255,8 +255,11 @@
   virtual void DidStopFlinging() {}
 
   // Returns the ID associated with the CompositorFrameSink of this view.
+  // TODO(fsamuel): Return by const ref.
   virtual viz::FrameSinkId GetFrameSinkId();
 
+  // Returns the LocalSurfaceId allocated by the parent client for this view.
+  // TODO(fsamuel): Return by const ref.
   virtual viz::LocalSurfaceId GetLocalSurfaceId() const;
 
   // When there are multiple RenderWidgetHostViews for a single page, input
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 dd2ad3e..48fd5c32 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
@@ -126,7 +126,7 @@
           parent_frame_sink_id_, frame_sink_id_);
     }
     parent_frame_sink_id_ = viz::FrameSinkId();
-    local_surface_id_ = viz::LocalSurfaceId();
+    last_received_local_surface_id_ = viz::LocalSurfaceId();
 
     // Unlocks the mouse if this RenderWidgetHostView holds the lock.
     UnlockMouse();
@@ -485,8 +485,9 @@
   DCHECK(result);
   has_frame_ = true;
 
-  if (local_surface_id_ != local_surface_id || HasEmbedderChanged()) {
-    local_surface_id_ = local_surface_id;
+  if (last_received_local_surface_id_ != local_surface_id ||
+      HasEmbedderChanged()) {
+    last_received_local_surface_id_ = local_surface_id;
     SendSurfaceInfoToEmbedder();
   }
 
@@ -506,7 +507,7 @@
   viz::SurfaceSequence sequence =
       viz::SurfaceSequence(frame_sink_id_, next_surface_sequence_++);
   viz::SurfaceManager* manager = GetFrameSinkManager()->surface_manager();
-  viz::SurfaceId surface_id(frame_sink_id_, local_surface_id_);
+  viz::SurfaceId surface_id(frame_sink_id_, last_received_local_surface_id_);
   // The renderer process will satisfy this dependency when it creates a
   // SurfaceLayer.
   if (!manager->using_surface_references())
@@ -592,6 +593,12 @@
   return frame_sink_id_;
 }
 
+viz::LocalSurfaceId RenderWidgetHostViewChildFrame::GetLocalSurfaceId() const {
+  if (frame_connector_)
+    return frame_connector_->local_surface_id();
+  return viz::LocalSurfaceId();
+}
+
 void RenderWidgetHostViewChildFrame::ProcessKeyboardEvent(
     const NativeWebKeyboardEvent& event,
     const ui::LatencyInfo& latency) {
@@ -634,11 +641,11 @@
 
 gfx::Point RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpace(
     const gfx::Point& point) {
-  if (!frame_connector_ || !local_surface_id_.is_valid())
+  if (!frame_connector_ || !last_received_local_surface_id_.is_valid())
     return point;
 
   return frame_connector_->TransformPointToRootCoordSpace(
-      point, viz::SurfaceId(frame_sink_id_, local_surface_id_));
+      point, viz::SurfaceId(frame_sink_id_, last_received_local_surface_id_));
 }
 
 bool RenderWidgetHostViewChildFrame::TransformPointToLocalCoordSpace(
@@ -646,19 +653,20 @@
     const viz::SurfaceId& original_surface,
     gfx::Point* transformed_point) {
   *transformed_point = point;
-  if (!frame_connector_ || !local_surface_id_.is_valid())
+  if (!frame_connector_ || !last_received_local_surface_id_.is_valid())
     return false;
 
   return frame_connector_->TransformPointToLocalCoordSpace(
       point, original_surface,
-      viz::SurfaceId(frame_sink_id_, local_surface_id_), transformed_point);
+      viz::SurfaceId(frame_sink_id_, last_received_local_surface_id_),
+      transformed_point);
 }
 
 bool RenderWidgetHostViewChildFrame::TransformPointToCoordSpaceForView(
     const gfx::Point& point,
     RenderWidgetHostViewBase* target_view,
     gfx::Point* transformed_point) {
-  if (!frame_connector_ || !local_surface_id_.is_valid())
+  if (!frame_connector_ || !last_received_local_surface_id_.is_valid())
     return false;
 
   if (target_view == this) {
@@ -667,7 +675,8 @@
   }
 
   return frame_connector_->TransformPointToCoordSpaceForView(
-      point, target_view, viz::SurfaceId(frame_sink_id_, local_surface_id_),
+      point, target_view,
+      viz::SurfaceId(frame_sink_id_, last_received_local_surface_id_),
       transformed_point);
 }
 
@@ -864,7 +873,7 @@
 }
 
 viz::SurfaceId RenderWidgetHostViewChildFrame::SurfaceIdForTesting() const {
-  return viz::SurfaceId(frame_sink_id_, local_surface_id_);
+  return viz::SurfaceId(frame_sink_id_, last_received_local_surface_id_);
 }
 
 void RenderWidgetHostViewChildFrame::CreateCompositorFrameSinkSupport() {
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.h b/content/browser/renderer_host/render_widget_host_view_child_frame.h
index 96e11aa..802ce0c 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.h
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.h
@@ -130,6 +130,7 @@
   bool LockMouse() override;
   void UnlockMouse() override;
   viz::FrameSinkId GetFrameSinkId() override;
+  viz::LocalSurfaceId GetLocalSurfaceId() const override;
   void ProcessKeyboardEvent(const NativeWebKeyboardEvent& event,
                             const ui::LatencyInfo& latency) override;
   void ProcessMouseEvent(const blink::WebMouseEvent& event,
@@ -247,7 +248,7 @@
 
   // Surface-related state.
   std::unique_ptr<viz::CompositorFrameSinkSupport> support_;
-  viz::LocalSurfaceId local_surface_id_;
+  viz::LocalSurfaceId last_received_local_surface_id_;
   uint32_t next_surface_sequence_;
   gfx::Size current_surface_size_;
   float current_surface_scale_factor_;
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
index 1f9161a3..23bdcf77 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -123,11 +123,12 @@
   }
 
   viz::SurfaceId GetSurfaceId() const {
-    return viz::SurfaceId(view_->frame_sink_id_, view_->local_surface_id_);
+    return viz::SurfaceId(view_->frame_sink_id_,
+                          view_->last_received_local_surface_id_);
   }
 
   viz::LocalSurfaceId GetLocalSurfaceId() const {
-    return view_->local_surface_id_;
+    return view_->last_received_local_surface_id_;
   }
 
   void ClearCompositorSurfaceIfNecessary() {
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 362290d..aed8fcf 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1030,14 +1030,16 @@
  private:
   ~FrameRectChangedMessageFilter() override {}
 
-  void OnFrameRectChanged(const gfx::Rect& rect) {
+  void OnFrameRectChanged(const gfx::Rect& rect,
+                          const viz::LocalSurfaceId& local_surface_id) {
     content::BrowserThread::PostTask(
         content::BrowserThread::UI, FROM_HERE,
         base::Bind(&FrameRectChangedMessageFilter::OnFrameRectChangedOnUI, this,
-                   rect));
+                   rect, local_surface_id));
   }
 
-  void OnFrameRectChangedOnUI(const gfx::Rect& rect) {
+  void OnFrameRectChangedOnUI(const gfx::Rect& rect,
+                              const viz::LocalSurfaceId& local_surface_id) {
     last_rect_ = rect;
     if (!frame_rect_received_) {
       frame_rect_received_ = true;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 29be3ff..119a5b751 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -177,8 +177,6 @@
     "input/input_event_struct_traits.h",
     "input/input_param_traits.cc",
     "input/input_param_traits.h",
-    "input/synthetic_gesture_packet.cc",
-    "input/synthetic_gesture_packet.h",
     "input/synthetic_gesture_params.cc",
     "input/synthetic_gesture_params.h",
     "input/synthetic_pinch_gesture_params.cc",
@@ -613,6 +611,7 @@
     "image_downloader/image_downloader.mojom",
     "indexed_db/indexed_db.mojom",
     "input/input_handler.mojom",
+    "input/input_injector.mojom",
     "leveldb_wrapper.mojom",
     "manifest_observer.mojom",
     "media/media_devices.mojom",
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 72a527a..b54c9d2 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -870,6 +870,10 @@
                     url::Origin /* origin */,
                     bool /* is potentially trustworthy unique origin */)
 
+// Notifies RenderFrameProxy that its associated RenderWidgetHostView has
+// changed.
+IPC_MESSAGE_ROUTED0(FrameMsg_ViewChanged)
+
 // Notifies this frame or proxy that it is now focused.  This is used to
 // support cross-process focused frame changes.
 IPC_MESSAGE_ROUTED0(FrameMsg_SetFocusedFrame)
@@ -1440,7 +1444,9 @@
 
 // Tells the parent that a child's frame rect has changed (or the rect/scroll
 // position of a child's ancestor has changed).
-IPC_MESSAGE_ROUTED1(FrameHostMsg_FrameRectChanged, gfx::Rect /* frame_rect */)
+IPC_MESSAGE_ROUTED2(FrameHostMsg_FrameRectChanged,
+                    gfx::Rect /* frame_rect */,
+                    viz::LocalSurfaceId /* local_surface_id */)
 
 // Sent by a parent frame to update its child's viewport intersection rect for
 // use by the IntersectionObserver API.
diff --git a/content/common/input/input_injector.mojom b/content/common/input/input_injector.mojom
new file mode 100644
index 0000000..d43891c
--- /dev/null
+++ b/content/common/input/input_injector.mojom
@@ -0,0 +1,18 @@
+// 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 content.mojom;
+
+import "content/common/native_types.mojom";
+
+// Host interface representing the ability to inject input
+// from a less priviledged application. Available when
+// --enable-gpu-benchmarking command line flag is provided.
+interface InputInjector {
+  QueueSyntheticSmoothDrag(SyntheticSmoothDrag drag) => ();
+  QueueSyntheticSmoothScroll(SyntheticSmoothScroll scroll) => ();
+  QueueSyntheticPinch(SyntheticPinch pinch) => ();
+  QueueSyntheticTap(SyntheticTap tap) => ();
+  QueueSyntheticPointerAction(SyntheticPointerAction pointer_action) => ();
+};
diff --git a/content/common/input/input_param_traits.cc b/content/common/input/input_param_traits.cc
index 1ff8fb2..0fe6100 100644
--- a/content/common/input/input_param_traits.cc
+++ b/content/common/input/input_param_traits.cc
@@ -15,18 +15,6 @@
 #include "ui/events/blink/web_input_event_traits.h"
 
 namespace IPC {
-namespace {
-template <typename GestureType>
-std::unique_ptr<content::SyntheticGestureParams> ReadGestureParams(
-    const base::Pickle* m,
-    base::PickleIterator* iter) {
-  std::unique_ptr<GestureType> gesture_params(new GestureType);
-  if (!ReadParam(m, iter, gesture_params.get()))
-    return std::unique_ptr<content::SyntheticGestureParams>();
-
-  return std::move(gesture_params);
-}
-}  // namespace
 
 void ParamTraits<ui::WebScopedInputEvent>::GetSize(base::PickleSizer* s,
                                                    const param_type& p) {
@@ -64,103 +52,4 @@
   LogParam(static_cast<WebInputEventPointer>(p.get()), l);
 }
 
-void ParamTraits<content::SyntheticGesturePacket>::Write(base::Pickle* m,
-                                                         const param_type& p) {
-  DCHECK(p.gesture_params());
-  WriteParam(m, p.gesture_params()->GetGestureType());
-  switch (p.gesture_params()->GetGestureType()) {
-    case content::SyntheticGestureParams::SMOOTH_SCROLL_GESTURE:
-      WriteParam(m, *content::SyntheticSmoothScrollGestureParams::Cast(
-          p.gesture_params()));
-      break;
-    case content::SyntheticGestureParams::SMOOTH_DRAG_GESTURE:
-      WriteParam(m, *content::SyntheticSmoothDragGestureParams::Cast(
-                 p.gesture_params()));
-      break;
-    case content::SyntheticGestureParams::PINCH_GESTURE:
-      WriteParam(m, *content::SyntheticPinchGestureParams::Cast(
-          p.gesture_params()));
-      break;
-    case content::SyntheticGestureParams::TAP_GESTURE:
-      WriteParam(m, *content::SyntheticTapGestureParams::Cast(
-          p.gesture_params()));
-      break;
-    case content::SyntheticGestureParams::POINTER_ACTION_LIST:
-      WriteParam(m, *content::SyntheticPointerActionListParams::Cast(
-                        p.gesture_params()));
-      break;
-  }
-}
-
-bool ParamTraits<content::SyntheticGesturePacket>::Read(
-    const base::Pickle* m,
-    base::PickleIterator* iter,
-    param_type* p) {
-  content::SyntheticGestureParams::GestureType gesture_type;
-  if (!ReadParam(m, iter, &gesture_type))
-    return false;
-  std::unique_ptr<content::SyntheticGestureParams> gesture_params;
-  switch (gesture_type) {
-    case content::SyntheticGestureParams::SMOOTH_SCROLL_GESTURE:
-      gesture_params =
-          ReadGestureParams<content::SyntheticSmoothScrollGestureParams>(m,
-                                                                         iter);
-      break;
-    case content::SyntheticGestureParams::SMOOTH_DRAG_GESTURE:
-      gesture_params =
-          ReadGestureParams<content::SyntheticSmoothDragGestureParams>(m, iter);
-      break;
-    case content::SyntheticGestureParams::PINCH_GESTURE:
-      gesture_params =
-          ReadGestureParams<content::SyntheticPinchGestureParams>(m, iter);
-      break;
-    case content::SyntheticGestureParams::TAP_GESTURE:
-      gesture_params =
-          ReadGestureParams<content::SyntheticTapGestureParams>(m, iter);
-      break;
-    case content::SyntheticGestureParams::POINTER_ACTION_LIST:
-      gesture_params =
-          ReadGestureParams<content::SyntheticPointerActionListParams>(m, iter);
-      break;
-    default:
-      return false;
-  }
-
-  p->set_gesture_params(std::move(gesture_params));
-  return p->gesture_params() != NULL;
-}
-
-void ParamTraits<content::SyntheticGesturePacket>::Log(const param_type& p,
-                                                       std::string* l) {
-  DCHECK(p.gesture_params());
-  switch (p.gesture_params()->GetGestureType()) {
-    case content::SyntheticGestureParams::SMOOTH_SCROLL_GESTURE:
-      LogParam(
-          *content::SyntheticSmoothScrollGestureParams::Cast(
-              p.gesture_params()),
-          l);
-      break;
-    case content::SyntheticGestureParams::SMOOTH_DRAG_GESTURE:
-      LogParam(
-          *content::SyntheticSmoothDragGestureParams::Cast(p.gesture_params()),
-          l);
-      break;
-    case content::SyntheticGestureParams::PINCH_GESTURE:
-      LogParam(
-          *content::SyntheticPinchGestureParams::Cast(p.gesture_params()),
-          l);
-      break;
-    case content::SyntheticGestureParams::TAP_GESTURE:
-      LogParam(
-          *content::SyntheticTapGestureParams::Cast(p.gesture_params()),
-          l);
-      break;
-    case content::SyntheticGestureParams::POINTER_ACTION_LIST:
-      LogParam(
-          *content::SyntheticPointerActionListParams::Cast(p.gesture_params()),
-          l);
-      break;
-  }
-}
-
 }  // namespace IPC
diff --git a/content/common/input/input_param_traits.h b/content/common/input/input_param_traits.h
index db6e82d..5c36652 100644
--- a/content/common/input/input_param_traits.h
+++ b/content/common/input/input_param_traits.h
@@ -10,7 +10,6 @@
 
 #include "content/common/content_export.h"
 #include "content/common/content_param_traits_macros.h"
-#include "content/common/input/synthetic_gesture_packet.h"
 #include "ui/events/blink/web_input_event_traits.h"
 
 namespace IPC {
@@ -26,16 +25,6 @@
   static void Log(const param_type& p, std::string* l);
 };
 
-template<>
-struct CONTENT_EXPORT ParamTraits<content::SyntheticGesturePacket> {
-  typedef content::SyntheticGesturePacket param_type;
-  static void Write(base::Pickle* m, const param_type& p);
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* r);
-  static void Log(const param_type& p, std::string* l);
-};
-
 }  // namespace IPC
 
 #endif  // CONTENT_COMMON_INPUT_INPUT_PARAM_TRAITS_H_
diff --git a/content/common/input/input_param_traits_unittest.cc b/content/common/input/input_param_traits_unittest.cc
index f8dc5e03..4bc88c5f 100644
--- a/content/common/input/input_param_traits_unittest.cc
+++ b/content/common/input/input_param_traits_unittest.cc
@@ -12,12 +12,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "content/common/input/input_event.h"
-#include "content/common/input/synthetic_gesture_params.h"
-#include "content/common/input/synthetic_pinch_gesture_params.h"
-#include "content/common/input/synthetic_pointer_action_list_params.h"
-#include "content/common/input/synthetic_pointer_action_params.h"
-#include "content/common/input/synthetic_smooth_drag_gesture_params.h"
-#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
 #include "content/common/input_messages.h"
 #include "ipc/ipc_message.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -50,99 +44,6 @@
       Compare((*a)[i].get(), (*b)[i].get());
   }
 
-  static void Compare(const SyntheticSmoothScrollGestureParams* a,
-                      const SyntheticSmoothScrollGestureParams* b) {
-    EXPECT_EQ(a->gesture_source_type, b->gesture_source_type);
-    EXPECT_EQ(a->anchor, b->anchor);
-    EXPECT_EQ(a->distances.size(), b->distances.size());
-    for (size_t i = 0; i < a->distances.size(); i++)
-      EXPECT_EQ(a->distances[i], b->distances[i]);
-    EXPECT_EQ(a->prevent_fling, b->prevent_fling);
-    EXPECT_EQ(a->speed_in_pixels_s, b->speed_in_pixels_s);
-  }
-
-  static void Compare(const SyntheticSmoothDragGestureParams* a,
-                      const SyntheticSmoothDragGestureParams* b) {
-    EXPECT_EQ(a->gesture_source_type, b->gesture_source_type);
-    EXPECT_EQ(a->start_point, b->start_point);
-    EXPECT_EQ(a->distances.size(), b->distances.size());
-    for (size_t i = 0; i < a->distances.size(); i++)
-      EXPECT_EQ(a->distances[i], b->distances[i]);
-    EXPECT_EQ(a->speed_in_pixels_s, b->speed_in_pixels_s);
-  }
-
-  static void Compare(const SyntheticPinchGestureParams* a,
-                      const SyntheticPinchGestureParams* b) {
-    EXPECT_EQ(a->gesture_source_type, b->gesture_source_type);
-    EXPECT_EQ(a->scale_factor, b->scale_factor);
-    EXPECT_EQ(a->anchor, b->anchor);
-    EXPECT_EQ(a->relative_pointer_speed_in_pixels_s,
-              b->relative_pointer_speed_in_pixels_s);
-  }
-
-  static void Compare(const SyntheticTapGestureParams* a,
-                      const SyntheticTapGestureParams* b) {
-    EXPECT_EQ(a->gesture_source_type, b->gesture_source_type);
-    EXPECT_EQ(a->position, b->position);
-    EXPECT_EQ(a->duration_ms, b->duration_ms);
-  }
-
-  static void Compare(const SyntheticPointerActionParams* a,
-                      const SyntheticPointerActionParams* b) {
-    EXPECT_EQ(a->pointer_action_type(), b->pointer_action_type());
-    if (a->pointer_action_type() ==
-            SyntheticPointerActionParams::PointerActionType::PRESS ||
-        a->pointer_action_type() ==
-            SyntheticPointerActionParams::PointerActionType::MOVE) {
-      EXPECT_EQ(a->position(), b->position());
-    }
-    EXPECT_EQ(a->index(), b->index());
-  }
-
-  static void Compare(const SyntheticPointerActionListParams* a,
-                      const SyntheticPointerActionListParams* b) {
-    EXPECT_EQ(a->gesture_source_type, b->gesture_source_type);
-    EXPECT_EQ(a->params.size(), b->params.size());
-    for (size_t i = 0; i < a->params.size(); ++i) {
-      EXPECT_EQ(a->params[i].size(), b->params[i].size());
-      for (size_t j = 0; j < a->params[i].size(); ++j) {
-        Compare(&a->params[i][j], &b->params[i][j]);
-      }
-    }
-  }
-
-  static void Compare(const SyntheticGesturePacket* a,
-                      const SyntheticGesturePacket* b) {
-    ASSERT_EQ(!!a, !!b);
-    if (!a) return;
-    ASSERT_EQ(!!a->gesture_params(), !!b->gesture_params());
-    if (!a->gesture_params()) return;
-    ASSERT_EQ(a->gesture_params()->GetGestureType(),
-              b->gesture_params()->GetGestureType());
-    switch (a->gesture_params()->GetGestureType()) {
-      case SyntheticGestureParams::SMOOTH_SCROLL_GESTURE:
-        Compare(SyntheticSmoothScrollGestureParams::Cast(a->gesture_params()),
-                SyntheticSmoothScrollGestureParams::Cast(b->gesture_params()));
-        break;
-      case SyntheticGestureParams::SMOOTH_DRAG_GESTURE:
-        Compare(SyntheticSmoothDragGestureParams::Cast(a->gesture_params()),
-                SyntheticSmoothDragGestureParams::Cast(b->gesture_params()));
-        break;
-      case SyntheticGestureParams::PINCH_GESTURE:
-        Compare(SyntheticPinchGestureParams::Cast(a->gesture_params()),
-                SyntheticPinchGestureParams::Cast(b->gesture_params()));
-        break;
-      case SyntheticGestureParams::TAP_GESTURE:
-        Compare(SyntheticTapGestureParams::Cast(a->gesture_params()),
-                SyntheticTapGestureParams::Cast(b->gesture_params()));
-        break;
-      case SyntheticGestureParams::POINTER_ACTION_LIST:
-        Compare(SyntheticPointerActionListParams::Cast(a->gesture_params()),
-                SyntheticPointerActionListParams::Cast(b->gesture_params()));
-        break;
-    }
-  }
-
   static void Verify(const InputEvents& events_in) {
     IPC::Message msg;
     IPC::ParamTraits<InputEvents>::Write(&msg, events_in);
@@ -161,27 +62,6 @@
     ASSERT_FALSE(events_in_string.empty());
     EXPECT_EQ(events_in_string, events_out_string);
   }
-
-  static void Verify(const SyntheticGesturePacket& packet_in) {
-    IPC::Message msg;
-    IPC::ParamTraits<SyntheticGesturePacket>::Write(&msg, packet_in);
-
-    SyntheticGesturePacket packet_out;
-    base::PickleIterator iter(msg);
-    EXPECT_TRUE(IPC::ParamTraits<SyntheticGesturePacket>::Read(&msg, &iter,
-                                                               &packet_out));
-
-    Compare(&packet_in, &packet_out);
-
-    // Perform a sanity check that logging doesn't explode.
-    std::string packet_in_string;
-    IPC::ParamTraits<SyntheticGesturePacket>::Log(packet_in, &packet_in_string);
-    std::string packet_out_string;
-    IPC::ParamTraits<SyntheticGesturePacket>::Log(packet_out,
-                                                  &packet_out_string);
-    ASSERT_FALSE(packet_in_string.empty());
-    EXPECT_EQ(packet_in_string, packet_out_string);
-  }
 };
 
 TEST_F(InputParamTraitsTest, UninitializedEvents) {
@@ -238,133 +118,5 @@
   Verify(events);
 }
 
-TEST_F(InputParamTraitsTest, InvalidSyntheticGestureParams) {
-  IPC::Message msg;
-  // Write invalid value for SyntheticGestureParams::GestureType.
-  WriteParam(&msg, -3);
-
-  SyntheticGesturePacket packet_out;
-  base::PickleIterator iter(msg);
-  ASSERT_FALSE(
-      IPC::ParamTraits<SyntheticGesturePacket>::Read(&msg, &iter, &packet_out));
-}
-
-TEST_F(InputParamTraitsTest, SyntheticSmoothScrollGestureParams) {
-  std::unique_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
-      new SyntheticSmoothScrollGestureParams);
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  gesture_params->anchor.SetPoint(234, 345);
-  gesture_params->distances.push_back(gfx::Vector2d(123, -789));
-  gesture_params->distances.push_back(gfx::Vector2d(-78, 43));
-  gesture_params->prevent_fling = false;
-  gesture_params->speed_in_pixels_s = 456;
-  ASSERT_EQ(SyntheticGestureParams::SMOOTH_SCROLL_GESTURE,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticPinchGestureParams) {
-  std::unique_ptr<SyntheticPinchGestureParams> gesture_params(
-      new SyntheticPinchGestureParams);
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  gesture_params->scale_factor = 2.3f;
-  gesture_params->anchor.SetPoint(234, 345);
-  gesture_params->relative_pointer_speed_in_pixels_s = 456;
-  ASSERT_EQ(SyntheticGestureParams::PINCH_GESTURE,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticTapGestureParams) {
-  std::unique_ptr<SyntheticTapGestureParams> gesture_params(
-      new SyntheticTapGestureParams);
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  gesture_params->position.SetPoint(798, 233);
-  gesture_params->duration_ms = 13;
-  ASSERT_EQ(SyntheticGestureParams::TAP_GESTURE,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticPointerActionListParamsMove) {
-  SyntheticPointerActionParams action_params(
-      SyntheticPointerActionParams::PointerActionType::MOVE);
-  action_params.set_position(gfx::PointF(356, 287));
-  action_params.set_index(0);
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params =
-      base::MakeUnique<SyntheticPointerActionListParams>();
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  gesture_params->PushPointerActionParams(action_params);
-
-  ASSERT_EQ(SyntheticGestureParams::POINTER_ACTION_LIST,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticPointerActionListParamsPressRelease) {
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params =
-      base::MakeUnique<SyntheticPointerActionListParams>();
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  SyntheticPointerActionParams action_params(
-      SyntheticPointerActionParams::PointerActionType::PRESS);
-  action_params.set_index(0);
-  gesture_params->PushPointerActionParams(action_params);
-  action_params.set_pointer_action_type(
-      SyntheticPointerActionParams::PointerActionType::RELEASE);
-  gesture_params->PushPointerActionParams(action_params);
-
-  ASSERT_EQ(SyntheticGestureParams::POINTER_ACTION_LIST,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticPointerActionParamsIdle) {
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params =
-      base::MakeUnique<SyntheticPointerActionListParams>();
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  SyntheticPointerActionParams action_params(
-      SyntheticPointerActionParams::PointerActionType::IDLE);
-  action_params.set_index(0);
-  gesture_params->PushPointerActionParams(action_params);
-
-  ASSERT_EQ(SyntheticGestureParams::POINTER_ACTION_LIST,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-  Verify(packet_in);
-}
-
-TEST_F(InputParamTraitsTest, SyntheticPointerActionListParamsTwoPresses) {
-  SyntheticPointerActionListParams::ParamList param_list;
-  SyntheticPointerActionParams action_params(
-      SyntheticPointerActionParams::PointerActionType::PRESS);
-  action_params.set_index(0);
-  param_list.push_back(action_params);
-  action_params.set_index(1);
-  param_list.push_back(action_params);
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params =
-      base::MakeUnique<SyntheticPointerActionListParams>(param_list);
-  gesture_params->gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-
-  ASSERT_EQ(SyntheticGestureParams::POINTER_ACTION_LIST,
-            gesture_params->GetGestureType());
-  SyntheticGesturePacket packet_in;
-  packet_in.set_gesture_params(std::move(gesture_params));
-  Verify(packet_in);
-}
-
 }  // namespace
 }  // namespace content
diff --git a/content/common/input/synthetic_gesture_packet.cc b/content/common/input/synthetic_gesture_packet.cc
deleted file mode 100644
index 1a999b0..0000000
--- a/content/common/input/synthetic_gesture_packet.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2013 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/common/input/synthetic_gesture_packet.h"
-
-namespace content {
-
-SyntheticGesturePacket::SyntheticGesturePacket() {}
-
-SyntheticGesturePacket::~SyntheticGesturePacket() {}
-
-}  // namespace content
diff --git a/content/common/input/synthetic_gesture_packet.h b/content/common/input/synthetic_gesture_packet.h
deleted file mode 100644
index 4f9b281d..0000000
--- a/content/common/input/synthetic_gesture_packet.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2013 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_COMMON_INPUT_SYNTHETIC_GESTURE_PACKET_H_
-#define CONTENT_COMMON_INPUT_SYNTHETIC_GESTURE_PACKET_H_
-
-#include <memory>
-#include <utility>
-
-#include "base/macros.h"
-#include "content/common/content_export.h"
-#include "content/common/input/synthetic_gesture_params.h"
-
-namespace content {
-
-// Wraps an object of type SyntheticGestureParams (or one of its subclasses) for
-// sending it over IPC.
-class CONTENT_EXPORT SyntheticGesturePacket {
- public:
-  SyntheticGesturePacket();
-  ~SyntheticGesturePacket();
-
-  void set_gesture_params(
-      std::unique_ptr<SyntheticGestureParams> gesture_params) {
-    gesture_params_ = std::move(gesture_params);
-  }
-  const SyntheticGestureParams* gesture_params() const {
-    return gesture_params_.get();
-  }
-  std::unique_ptr<SyntheticGestureParams> pass_gesture_params() {
-    return std::move(gesture_params_);
-  }
-
- private:
-  std::unique_ptr<SyntheticGestureParams> gesture_params_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyntheticGesturePacket);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_INPUT_SYNTHETIC_GESTURE_PACKET_H_
diff --git a/content/common/input/synthetic_gesture_params.h b/content/common/input/synthetic_gesture_params.h
index 7dc59fa..9f5dfc47 100644
--- a/content/common/input/synthetic_gesture_params.h
+++ b/content/common/input/synthetic_gesture_params.h
@@ -11,23 +11,12 @@
 
 namespace content {
 
-// Base class for storing parameters of synthetic gestures. Sending an object
-// over IPC is handled by encapsulating it in a SyntheticGesturePacket object.
+// Base class for storing parameters of synthetic gestures.
 //
 // The subclasses of this class only store data on synthetic gestures.
 // The logic for dispatching input events that implement the gesture lives
 // in separate classes in content/browser/renderer_host/input/.
 //
-// Adding new gesture types involves the following steps:
-//   1) Create a new sub-type of SyntheticGestureParams with the parameters
-//      needed for the new gesture.
-//   2) Use IPC macros to create serialization methods for the new type in
-//      content/common/input_messages.h.
-//   3) Extend ParamTraits<content::SyntheticGesturePacket>::Write/Read/Log in
-//      content/common/input/input_param_traits.cc.
-//   4) Add a new unit test to make sure that sending the type over IPC works
-//      correctly.
-// The details of each step should become clear when looking at other types.
 struct CONTENT_EXPORT SyntheticGestureParams {
   SyntheticGestureParams();
   SyntheticGestureParams(const SyntheticGestureParams& other);
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index 73dedb03..58a46ad 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -18,7 +18,6 @@
 #include "content/common/input/input_event_ack_state.h"
 #include "content/common/input/input_event_dispatch_type.h"
 #include "content/common/input/input_param_traits.h"
-#include "content/common/input/synthetic_gesture_packet.h"
 #include "content/common/input/synthetic_gesture_params.h"
 #include "content/common/input/synthetic_pinch_gesture_params.h"
 #include "content/common/input/synthetic_pointer_action_list_params.h"
@@ -309,8 +308,6 @@
                     bool /* immediate_request */,
                     bool /* monitor_updates */)
 
-IPC_MESSAGE_ROUTED0(InputMsg_SyntheticGestureCompleted)
-
 // -----------------------------------------------------------------------------
 // Messages sent from the renderer to the browser.
 
@@ -318,9 +315,6 @@
 IPC_MESSAGE_ROUTED1(InputHostMsg_HandleInputEvent_ACK,
                     content::InputEventAck /* ack */)
 
-IPC_MESSAGE_ROUTED1(InputHostMsg_QueueSyntheticGesture,
-                    content::SyntheticGesturePacket)
-
 // Notifies the allowed touch actions for a new touch point.
 IPC_MESSAGE_ROUTED1(InputHostMsg_SetTouchAction,
                     cc::TouchAction /* touch_action */)
diff --git a/content/common/native_types.mojom b/content/common/native_types.mojom
index cd0e90f..61d28a2e 100644
--- a/content/common/native_types.mojom
+++ b/content/common/native_types.mojom
@@ -79,3 +79,18 @@
 
 [Native]
 struct DidOverscrollParams;
+
+[Native]
+struct SyntheticSmoothDrag;
+
+[Native]
+struct SyntheticSmoothScroll;
+
+[Native]
+struct SyntheticPinch;
+
+[Native]
+struct SyntheticTap;
+
+[Native]
+struct SyntheticPointerAction;
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap
index 94240ef..2d463f1b 100644
--- a/content/common/native_types.typemap
+++ b/content/common/native_types.typemap
@@ -11,6 +11,11 @@
   "//content/common/input/input_event.h",
   "//content/common/input/input_event_ack_source.h",
   "//content/common/input/input_event_ack_state.h",
+  "//content/common/input/synthetic_pinch_gesture_params.h",
+  "//content/common/input/synthetic_pointer_action_list_params.h",
+  "//content/common/input/synthetic_smooth_drag_gesture_params.h",
+  "//content/common/input/synthetic_smooth_scroll_gesture_params.h",
+  "//content/common/input/synthetic_tap_gesture_params.h",
   "//content/public/common/renderer_preferences.h",
   "//content/public/common/web_preferences.h",
   "//net/base/network_change_notifier.h",
@@ -73,6 +78,11 @@
   "content.mojom.RendererPreferences=content::RendererPreferences",
   "content.mojom.ResizeParams=content::ResizeParams",
   "content.mojom.ScrollUnits=blink::WebGestureEvent::ScrollUnits",
+  "content.mojom.SyntheticSmoothDrag=content::SyntheticSmoothDragGestureParams",
+  "content.mojom.SyntheticSmoothScroll=content::SyntheticSmoothScrollGestureParams",
+  "content.mojom.SyntheticPinch=content::SyntheticPinchGestureParams",
+  "content.mojom.SyntheticTap=content::SyntheticTapGestureParams",
+  "content.mojom.SyntheticPointerAction=content::SyntheticPointerActionListParams",
   "content.mojom.TouchState=blink::WebTouchPoint::State",
   "content.mojom.WebPopupType=blink::WebPopupType",
   "content.mojom.WebPreferences=content::WebPreferences",
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index b84e3468..6b45cdac 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -127,6 +127,9 @@
           // TODO(beng): figure out how to overlay test interfaces like this.
           "content::mojom::BrowserTarget",
 
+          // InputInjector is only exposed when gpu benchmarking is enabled.
+          "content::mojom::InputInjector",
+
           "content::mojom::RendererAudioOutputStreamFactory",
           "device::mojom::Geolocation",
           "device::mojom::GeolocationService",
diff --git a/content/public/common/resource_request.h b/content/public/common/resource_request.h
index 2f767350a..f776dfc 100644
--- a/content/public/common/resource_request.h
+++ b/content/public/common/resource_request.h
@@ -33,7 +33,7 @@
   ~ResourceRequest();
 
   // The request method: GET, POST, etc.
-  std::string method;
+  std::string method = "GET";
 
   // The absolute requested URL encoded in ASCII per the rules of RFC-2396.
   GURL url;
diff --git a/content/public/common/simple_url_loader_unittest.cc b/content/public/common/simple_url_loader_unittest.cc
index 061586c0..6e43aad4 100644
--- a/content/public/common/simple_url_loader_unittest.cc
+++ b/content/public/common/simple_url_loader_unittest.cc
@@ -79,7 +79,6 @@
   void RunRequestForURL(mojom::URLLoaderFactory* url_loader_factory,
                         const GURL& url) {
     ResourceRequest resource_request;
-    resource_request.method = "GET";
     resource_request.url = url;
     RunRequest(url_loader_factory, resource_request);
   }
@@ -101,7 +100,6 @@
       const GURL& url,
       size_t max_size) {
     ResourceRequest resource_request;
-    resource_request.method = "GET";
     resource_request.url = url;
     RunRequestWithBoundedSize(url_loader_factory, resource_request, max_size);
   }
@@ -238,7 +236,6 @@
 
 TEST_F(SimpleURLLoaderTest, BasicRequest) {
   ResourceRequest resource_request;
-  resource_request.method = "GET";
   // Use a more interesting request than "/echo", just to verify more than the
   // request URL is hooked up.
   resource_request.url = test_server_.GetURL("/echoheader?foo");
@@ -255,7 +252,6 @@
 // Test that SimpleURLLoader handles data URLs, which don't have headers.
 TEST_F(SimpleURLLoaderTest, DataURL) {
   ResourceRequest resource_request;
-  resource_request.method = "GET";
   // Use a more interesting request than "/echo", just to verify more than the
   // request URL is hooked up.
   resource_request.url = GURL("data:text/plain,foo");
@@ -273,7 +269,6 @@
 // different.
 TEST_F(SimpleURLLoaderTest, GzipBody) {
   ResourceRequest resource_request;
-  resource_request.method = "GET";
   resource_request.url = test_server_.GetURL("/gzip-body?foo");
   WaitForStringHelper string_helper;
   string_helper.RunRequest(url_loader_factory_.get(), resource_request);
@@ -562,7 +557,6 @@
 // before the request is even made, for that matter).
 TEST_F(SimpleURLLoaderTest, DestroyServiceBeforeResponseStarts) {
   ResourceRequest resource_request;
-  resource_request.method = "GET";
   resource_request.url = test_server_.GetURL("/hung");
   WaitForStringHelper string_helper;
   string_helper.simple_url_loader()
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 9e281e4..b779cc0 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -132,7 +132,8 @@
 
   EnableCompositing(true);
   DCHECK(compositing_helper_.get());
-  compositing_helper_->OnSetSurface(surface_info, sequence);
+  compositing_helper_->SetPrimarySurfaceInfo(surface_info);
+  compositing_helper_->SetFallbackSurfaceInfo(surface_info, sequence);
 }
 
 void BrowserPlugin::SendSatisfySequence(const viz::SurfaceSequence& sequence) {
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc
index f72c341..8fad0fb1 100644
--- a/content/renderer/child_frame_compositing_helper.cc
+++ b/content/renderer/child_frame_compositing_helper.cc
@@ -208,18 +208,18 @@
 }
 
 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
-    const gfx::Size& new_size,
-    float device_scale_factor,
+    const viz::SurfaceInfo& surface_info,
     cc::Layer* layer) {
-  if (buffer_size_ != new_size) {
-    buffer_size_ = new_size;
-    // The container size is in DIP, so is the layer size.
-    // Buffer size is in physical pixels, so we need to adjust
-    // it by the device scale factor.
-    gfx::Size device_scale_adjusted_size =
-        gfx::ScaleToFlooredSize(buffer_size_, 1.0f / device_scale_factor);
-    layer->SetBounds(device_scale_adjusted_size);
-  }
+  if (last_surface_size_in_pixels_ == surface_info.size_in_pixels())
+    return;
+
+  last_surface_size_in_pixels_ = surface_info.size_in_pixels();
+  // The container size is in DIP, so is the layer size.
+  // Buffer size is in physical pixels, so we need to adjust
+  // it by the device scale factor.
+  gfx::Size device_scale_adjusted_size = gfx::ScaleToFlooredSize(
+      surface_info.size_in_pixels(), 1.0f / surface_info.device_scale_factor());
+  layer->SetBounds(device_scale_adjusted_size);
 }
 
 void ChildFrameCompositingHelper::OnContainerDestroy() {
@@ -259,13 +259,42 @@
   UpdateWebLayer(std::move(layer));
 }
 
-void ChildFrameCompositingHelper::OnSetSurface(
+void ChildFrameCompositingHelper::SetPrimarySurfaceInfo(
+    const viz::SurfaceInfo& surface_info) {
+  last_primary_surface_id_ = surface_info.id();
+  float scale_factor = surface_info.device_scale_factor();
+  // TODO(oshima): This is a stopgap fix so that the compositor does not
+  // scaledown the content when 2x frame data is added to 1x parent frame data.
+  // Fix this in cc/.
+  if (IsUseZoomForDSFEnabled())
+    scale_factor = 1.0f;
+
+  surface_layer_ = cc::SurfaceLayer::Create(surface_reference_factory_);
+  surface_layer_->SetMasksToBounds(true);
+
+  viz::SurfaceInfo modified_surface_info(surface_info.id(), scale_factor,
+                                         surface_info.size_in_pixels());
+  surface_layer_->SetPrimarySurfaceInfo(modified_surface_info);
+
+  std::unique_ptr<cc_blink::WebLayerImpl> layer(
+      new cc_blink::WebLayerImpl(surface_layer_));
+  // TODO(lfg): Investigate if it's possible to propagate the information about
+  // the child surface's opacity. https://crbug.com/629851.
+  layer->SetOpaque(false);
+  layer->SetContentsOpaqueIsFixed(true);
+  UpdateWebLayer(std::move(layer));
+
+  UpdateVisibility(true);
+
+  CheckSizeAndAdjustLayerProperties(
+      surface_info,
+      static_cast<cc_blink::WebLayerImpl*>(web_layer_.get())->layer());
+}
+
+void ChildFrameCompositingHelper::SetFallbackSurfaceInfo(
     const viz::SurfaceInfo& surface_info,
     const viz::SurfaceSequence& sequence) {
   float scale_factor = surface_info.device_scale_factor();
-  surface_id_ = surface_info.id();
-  scoped_refptr<cc::SurfaceLayer> surface_layer =
-      cc::SurfaceLayer::Create(surface_reference_factory_);
   // TODO(oshima): This is a stopgap fix so that the compositor does not
   // scaledown the content when 2x frame data is added to 1x parent frame data.
   // Fix this in cc/.
@@ -289,22 +318,7 @@
 
   viz::SurfaceInfo modified_surface_info(surface_info.id(), scale_factor,
                                          surface_info.size_in_pixels());
-  surface_layer->SetPrimarySurfaceInfo(modified_surface_info);
-  surface_layer->SetFallbackSurfaceInfo(modified_surface_info);
-  surface_layer->SetMasksToBounds(true);
-  std::unique_ptr<cc_blink::WebLayerImpl> layer(
-      new cc_blink::WebLayerImpl(surface_layer));
-  // TODO(lfg): Investigate if it's possible to propagate the information about
-  // the child surface's opacity. https://crbug.com/629851.
-  layer->SetOpaque(false);
-  layer->SetContentsOpaqueIsFixed(true);
-  UpdateWebLayer(std::move(layer));
-
-  UpdateVisibility(true);
-
-  CheckSizeAndAdjustLayerProperties(
-      surface_info.size_in_pixels(), surface_info.device_scale_factor(),
-      static_cast<cc_blink::WebLayerImpl*>(web_layer_.get())->layer());
+  surface_layer_->SetFallbackSurfaceInfo(modified_surface_info);
 }
 
 void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
diff --git a/content/renderer/child_frame_compositing_helper.h b/content/renderer/child_frame_compositing_helper.h
index a9b99d3..4346ec9 100644
--- a/content/renderer/child_frame_compositing_helper.h
+++ b/content/renderer/child_frame_compositing_helper.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
 #include "base/memory/weak_ptr.h"
+#include "cc/layers/surface_layer.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/common/surfaces/surface_reference_factory.h"
 #include "content/common/content_export.h"
@@ -54,12 +55,13 @@
       RenderFrameProxy* render_frame_proxy);
 
   void OnContainerDestroy();
-  void OnSetSurface(const viz::SurfaceInfo& surface_info,
-                    const viz::SurfaceSequence& sequence);
+  void SetPrimarySurfaceInfo(const viz::SurfaceInfo& surface_info);
+  void SetFallbackSurfaceInfo(const viz::SurfaceInfo& surface_info,
+                              const viz::SurfaceSequence& sequence);
   void UpdateVisibility(bool);
   void ChildFrameGone();
 
-  viz::SurfaceId surface_id() const { return surface_id_; }
+  const viz::SurfaceId& surface_id() const { return last_primary_surface_id_; }
 
  protected:
   // Friend RefCounted so that the dtor can be non-public.
@@ -76,14 +78,14 @@
 
   blink::WebPluginContainer* GetContainer();
 
-  void CheckSizeAndAdjustLayerProperties(const gfx::Size& new_size,
-                                         float device_scale_factor,
+  void CheckSizeAndAdjustLayerProperties(const viz::SurfaceInfo& surface_info,
                                          cc::Layer* layer);
   void UpdateWebLayer(std::unique_ptr<blink::WebLayer> layer);
 
   const int host_routing_id_;
 
-  gfx::Size buffer_size_;
+  viz::SurfaceId last_primary_surface_id_;
+  gfx::Size last_surface_size_in_pixels_;
 
   // The lifetime of this weak pointer should be greater than the lifetime of
   // other member objects, as they may access this pointer during their
@@ -91,8 +93,8 @@
   const base::WeakPtr<BrowserPlugin> browser_plugin_;
   RenderFrameProxy* const render_frame_proxy_;
 
+  scoped_refptr<cc::SurfaceLayer> surface_layer_;
   std::unique_ptr<blink::WebLayer> web_layer_;
-  viz::SurfaceId surface_id_;
   blink::WebRemoteFrame* frame_;
 
   // If surface references are enabled use a stub reference factory.
diff --git a/content/renderer/gpu/actions_parser.cc b/content/renderer/gpu/actions_parser.cc
index 0d3c654..615baba 100644
--- a/content/renderer/gpu/actions_parser.cc
+++ b/content/renderer/gpu/actions_parser.cc
@@ -81,10 +81,7 @@
     action_index_++;
   }
 
-  if (!gesture_params_)
-    gesture_params_ = base::MakeUnique<SyntheticPointerActionListParams>();
-
-  gesture_params_->gesture_source_type =
+  gesture_params_.gesture_source_type =
       ToSyntheticGestureSourceType(source_type_);
   // Group a list of actions from all pointers into a
   // SyntheticPointerActionListParams object, which is a list of actions, which
@@ -96,7 +93,7 @@
       if (action_index < pointer_list.size())
         param_list.push_back(pointer_list[action_index]);
     }
-    gesture_params_->PushPointerActionParamsList(param_list);
+    gesture_params_.PushPointerActionParamsList(param_list);
   }
 
   return true;
diff --git a/content/renderer/gpu/actions_parser.h b/content/renderer/gpu/actions_parser.h
index 7aa0bf8..5939685f 100644
--- a/content/renderer/gpu/actions_parser.h
+++ b/content/renderer/gpu/actions_parser.h
@@ -27,8 +27,8 @@
   ~ActionsParser();
   bool ParsePointerActionSequence();
   std::string error_message() { return error_message_; }
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params() {
-    return std::move(gesture_params_);
+  const SyntheticPointerActionListParams& gesture_params() const {
+    return gesture_params_;
   }
 
  private:
@@ -37,7 +37,7 @@
   bool ParseAction(const base::DictionaryValue& action,
                    SyntheticPointerActionListParams::ParamList& param_list);
 
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params_;
+  SyntheticPointerActionListParams gesture_params_;
   std::vector<SyntheticPointerActionListParams::ParamList>
       pointer_actions_list_;
   size_t longest_action_sequence_;
@@ -52,4 +52,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_GPU_ACTION_PARSER_H_
\ No newline at end of file
+#endif  // CONTENT_RENDERER_GPU_ACTION_PARSER_H_
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.cc b/content/renderer/gpu/gpu_benchmarking_extension.cc
index f15bd60..820ffab 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu/gpu_benchmarking_extension.cc
@@ -42,6 +42,7 @@
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/WebKit/public/platform/WebMouseEvent.h"
 #include "third_party/WebKit/public/web/WebImageCache.h"
 #include "third_party/WebKit/public/web/WebKit.h"
@@ -334,6 +335,7 @@
 }
 
 bool BeginSmoothScroll(v8::Isolate* isolate,
+                       mojom::InputInjectorPtr& injector,
                        float pixels_to_scroll,
                        v8::Local<v8::Function> callback,
                        int gesture_source_type,
@@ -370,22 +372,21 @@
       new CallbackAndContext(isolate, callback,
                              context.web_frame()->MainWorldScriptContext());
 
-  std::unique_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
-      new SyntheticSmoothScrollGestureParams);
+  SyntheticSmoothScrollGestureParams gesture_params;
 
   if (gesture_source_type < 0 ||
       gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
     return false;
   }
-  gesture_params->gesture_source_type =
+  gesture_params.gesture_source_type =
       static_cast<SyntheticGestureParams::GestureSourceType>(
           gesture_source_type);
 
-  gesture_params->speed_in_pixels_s = speed_in_pixels_s;
-  gesture_params->prevent_fling = prevent_fling;
+  gesture_params.speed_in_pixels_s = speed_in_pixels_s;
+  gesture_params.prevent_fling = prevent_fling;
 
-  gesture_params->anchor.SetPoint(start_x * page_scale_factor,
-                                  start_y * page_scale_factor);
+  gesture_params.anchor.SetPoint(start_x * page_scale_factor,
+                                 start_y * page_scale_factor);
 
   float distance_length = pixels_to_scroll * page_scale_factor;
   gfx::Vector2dF distance;
@@ -412,20 +413,17 @@
   } else {
     return false;
   }
-  gesture_params->distances.push_back(distance);
+  gesture_params.distances.push_back(distance);
 
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  injector->QueueSyntheticSmoothScroll(
+      gesture_params, base::BindOnce(&OnSyntheticGestureCompleted,
+                                     base::RetainedRef(callback_and_context)));
 
   return true;
 }
 
 bool BeginSmoothDrag(v8::Isolate* isolate,
+                     mojom::InputInjectorPtr& injector,
                      float start_x,
                      float start_y,
                      float end_x,
@@ -440,30 +438,25 @@
       new CallbackAndContext(isolate, callback,
                              context.web_frame()->MainWorldScriptContext());
 
-  std::unique_ptr<SyntheticSmoothDragGestureParams> gesture_params(
-      new SyntheticSmoothDragGestureParams);
+  SyntheticSmoothDragGestureParams gesture_params;
 
   // Convert coordinates from CSS pixels to density independent pixels (DIPs).
   float page_scale_factor = context.web_view()->PageScaleFactor();
 
-  gesture_params->start_point.SetPoint(start_x * page_scale_factor,
-                                       start_y * page_scale_factor);
+  gesture_params.start_point.SetPoint(start_x * page_scale_factor,
+                                      start_y * page_scale_factor);
   gfx::PointF end_point(end_x * page_scale_factor,
                         end_y * page_scale_factor);
-  gfx::Vector2dF distance = end_point - gesture_params->start_point;
-  gesture_params->distances.push_back(distance);
-  gesture_params->speed_in_pixels_s = speed_in_pixels_s * page_scale_factor;
-  gesture_params->gesture_source_type =
+  gfx::Vector2dF distance = end_point - gesture_params.start_point;
+  gesture_params.distances.push_back(distance);
+  gesture_params.speed_in_pixels_s = speed_in_pixels_s * page_scale_factor;
+  gesture_params.gesture_source_type =
       static_cast<SyntheticGestureParams::GestureSourceType>(
           gesture_source_type);
 
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  injector->QueueSyntheticSmoothDrag(
+      gesture_params, base::BindOnce(&OnSyntheticGestureCompleted,
+                                     base::RetainedRef(callback_and_context)));
 
   return true;
 }
@@ -545,17 +538,18 @@
 gin::WrapperInfo GpuBenchmarking::kWrapperInfo = {gin::kEmbedderNativeGin};
 
 // static
-void GpuBenchmarking::Install(blink::WebLocalFrame* frame) {
+void GpuBenchmarking::Install(RenderFrameImpl* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
-  v8::Local<v8::Context> context = frame->MainWorldScriptContext();
+  v8::Local<v8::Context> context =
+      frame->GetWebFrame()->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
 
   v8::Context::Scope context_scope(context);
 
   gin::Handle<GpuBenchmarking> controller =
-      gin::CreateHandle(isolate, new GpuBenchmarking());
+      gin::CreateHandle(isolate, new GpuBenchmarking(frame));
   if (controller.IsEmpty())
     return;
 
@@ -564,7 +558,9 @@
   chrome->Set(gin::StringToV8(isolate, "gpuBenchmarking"), controller.ToV8());
 }
 
-GpuBenchmarking::GpuBenchmarking() {
+GpuBenchmarking::GpuBenchmarking(RenderFrameImpl* frame) {
+  frame->GetRemoteInterfaces()->GetInterface(
+      mojo::MakeRequest(&input_injector_));
 }
 
 GpuBenchmarking::~GpuBenchmarking() {
@@ -702,15 +698,9 @@
     return false;
   }
 
-  return BeginSmoothScroll(args->isolate(),
-                           pixels_to_scroll,
-                           callback,
-                           gesture_source_type,
-                           direction,
-                           speed_in_pixels_s,
-                           true,
-                           start_x,
-                           start_y);
+  return BeginSmoothScroll(args->isolate(), input_injector_, pixels_to_scroll,
+                           callback, gesture_source_type, direction,
+                           speed_in_pixels_s, true, start_x, start_y);
 }
 
 bool GpuBenchmarking::SmoothDrag(gin::Arguments* args) {
@@ -736,13 +726,8 @@
     return false;
   }
 
-  return BeginSmoothDrag(args->isolate(),
-                         start_x,
-                         start_y,
-                         end_x,
-                         end_y,
-                         callback,
-                         gesture_source_type,
+  return BeginSmoothDrag(args->isolate(), input_injector_, start_x, start_y,
+                         end_x, end_y, callback, gesture_source_type,
                          speed_in_pixels_s);
 }
 
@@ -770,15 +755,10 @@
     return false;
   }
 
-  return BeginSmoothScroll(args->isolate(),
-                           -pixels_to_scroll,
-                           callback,
-                           1,  // TOUCH_INPUT
-                           direction,
-                           speed_in_pixels_s,
-                           false,
-                           start_x,
-                           start_y);
+  return BeginSmoothScroll(
+      args->isolate(), input_injector_, -pixels_to_scroll, callback,
+      1,  // TOUCH_INPUT
+      direction, speed_in_pixels_s, false, start_x, start_y);
 }
 
 bool GpuBenchmarking::ScrollBounce(gin::Arguments* args) {
@@ -813,13 +793,12 @@
       new CallbackAndContext(args->isolate(), callback,
                              context.web_frame()->MainWorldScriptContext());
 
-  std::unique_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
-      new SyntheticSmoothScrollGestureParams);
+  SyntheticSmoothScrollGestureParams gesture_params;
 
-  gesture_params->speed_in_pixels_s = speed_in_pixels_s;
+  gesture_params.speed_in_pixels_s = speed_in_pixels_s;
 
-  gesture_params->anchor.SetPoint(start_x * page_scale_factor,
-                                  start_y * page_scale_factor);
+  gesture_params.anchor.SetPoint(start_x * page_scale_factor,
+                                 start_y * page_scale_factor);
 
   distance_length *= page_scale_factor;
   overscroll_length *= page_scale_factor;
@@ -842,17 +821,12 @@
   }
 
   for (int i = 0; i < repeat_count; i++) {
-    gesture_params->distances.push_back(distance);
-    gesture_params->distances.push_back(-distance + overscroll);
+    gesture_params.distances.push_back(distance);
+    gesture_params.distances.push_back(-distance + overscroll);
   }
-
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  input_injector_->QueueSyntheticSmoothScroll(
+      gesture_params, base::BindOnce(&OnSyntheticGestureCompleted,
+                                     base::RetainedRef(callback_and_context)));
 
   return true;
 }
@@ -877,30 +851,24 @@
     return false;
   }
 
-  std::unique_ptr<SyntheticPinchGestureParams> gesture_params(
-      new SyntheticPinchGestureParams);
+  SyntheticPinchGestureParams gesture_params;
 
   // TODO(bokan): Remove page scale here when change land in Catapult.
   // Convert coordinates from CSS pixels to density independent pixels (DIPs).
   float page_scale_factor = context.web_view()->PageScaleFactor();
 
-  gesture_params->scale_factor = scale_factor;
-  gesture_params->anchor.SetPoint(anchor_x * page_scale_factor,
-                                  anchor_y * page_scale_factor);
-  gesture_params->relative_pointer_speed_in_pixels_s =
+  gesture_params.scale_factor = scale_factor;
+  gesture_params.anchor.SetPoint(anchor_x * page_scale_factor,
+                                 anchor_y * page_scale_factor);
+  gesture_params.relative_pointer_speed_in_pixels_s =
       relative_pointer_speed_in_pixels_s;
 
   scoped_refptr<CallbackAndContext> callback_and_context =
       new CallbackAndContext(args->isolate(), callback,
                              context.web_frame()->MainWorldScriptContext());
-
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  input_injector_->QueueSyntheticPinch(
+      gesture_params, base::BindOnce(&OnSyntheticGestureCompleted,
+                                     base::RetainedRef(callback_and_context)));
 
   return true;
 }
@@ -981,32 +949,26 @@
     return false;
   }
 
-  std::unique_ptr<SyntheticTapGestureParams> gesture_params(
-      new SyntheticTapGestureParams);
+  SyntheticTapGestureParams gesture_params;
 
-  gesture_params->position.SetPoint(position_x * page_scale_factor,
-                                    position_y * page_scale_factor);
-  gesture_params->duration_ms = duration_ms;
+  gesture_params.position.SetPoint(position_x * page_scale_factor,
+                                   position_y * page_scale_factor);
+  gesture_params.duration_ms = duration_ms;
 
   if (gesture_source_type < 0 ||
       gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
     return false;
   }
-  gesture_params->gesture_source_type =
+  gesture_params.gesture_source_type =
       static_cast<SyntheticGestureParams::GestureSourceType>(
           gesture_source_type);
 
   scoped_refptr<CallbackAndContext> callback_and_context =
       new CallbackAndContext(args->isolate(), callback,
                              context.web_frame()->MainWorldScriptContext());
-
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  input_injector_->QueueSyntheticTap(
+      gesture_params, base::BindOnce(&OnSyntheticGestureCompleted,
+                                     base::RetainedRef(callback_and_context)));
 
   return true;
 }
@@ -1035,9 +997,6 @@
   if (!actions_parser.ParsePointerActionSequence())
     return false;
 
-  std::unique_ptr<SyntheticPointerActionListParams> gesture_params =
-      actions_parser.gesture_params();
-
   if (!GetOptionalArg(args, &callback)) {
     args->ThrowError();
     return false;
@@ -1047,13 +1006,10 @@
   scoped_refptr<CallbackAndContext> callback_and_context =
       new CallbackAndContext(args->isolate(), callback,
                              context.web_frame()->MainWorldScriptContext());
-  // TODO(678879): If the render_view_impl is destroyed while the gesture is in
-  // progress, we will leak the callback and context. This needs to be fixed,
-  // somehow, see https://crbug.com/678879.
-  context.render_view_impl()->GetWidget()->QueueSyntheticGesture(
-      std::move(gesture_params),
-      base::Bind(&OnSyntheticGestureCompleted,
-                 base::RetainedRef(callback_and_context)));
+  input_injector_->QueueSyntheticPointerAction(
+      actions_parser.gesture_params(),
+      base::BindOnce(&OnSyntheticGestureCompleted,
+                     base::RetainedRef(callback_and_context)));
   return true;
 }
 
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.h b/content/renderer/gpu/gpu_benchmarking_extension.h
index fbb0022c..8d03051f 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.h
+++ b/content/renderer/gpu/gpu_benchmarking_extension.h
@@ -6,12 +6,9 @@
 #define CONTENT_RENDERER_GPU_GPU_BENCHMARKING_EXTENSION_H_
 
 #include "base/macros.h"
+#include "content/common/input/input_injector.mojom.h"
 #include "gin/wrappable.h"
 
-namespace blink {
-class WebLocalFrame;
-}
-
 namespace gin {
 class Arguments;
 }
@@ -23,14 +20,16 @@
 
 namespace content {
 
+class RenderFrameImpl;
+
 // gin class for gpu benchmarking
 class GpuBenchmarking : public gin::Wrappable<GpuBenchmarking> {
  public:
   static gin::WrapperInfo kWrapperInfo;
-  static void Install(blink::WebLocalFrame* frame);
+  static void Install(RenderFrameImpl* frame);
 
  private:
-  GpuBenchmarking();
+  explicit GpuBenchmarking(RenderFrameImpl* frame);
   ~GpuBenchmarking() override;
 
   // gin::Wrappable.
@@ -65,6 +64,7 @@
   bool HasGpuProcess();
   void GetGpuDriverBugWorkarounds(gin::Arguments* args);
 
+  mojom::InputInjectorPtr input_injector_;
   DISALLOW_COPY_AND_ASSIGN(GpuBenchmarking);
 };
 
diff --git a/content/renderer/media/audio_renderer_mixer_manager_unittest.cc b/content/renderer/media/audio_renderer_mixer_manager_unittest.cc
index c30b4d3..5672d52 100644
--- a/content/renderer/media/audio_renderer_mixer_manager_unittest.cc
+++ b/content/renderer/media/audio_renderer_mixer_manager_unittest.cc
@@ -740,7 +740,7 @@
   EXPECT_EQ(output_sample_rate,
             mixer->GetOutputParamsForTesting().sample_rate());
 
-#if defined(OS_LINUX) || defined(OS_MACOSX)
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA)
   // Use 10 ms buffer (441 frames per buffer).
   EXPECT_EQ(output_sample_rate / 100,
             mixer->GetOutputParamsForTesting().frames_per_buffer());
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index ff9f5a7d..2540e89 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3817,7 +3817,7 @@
       *base::CommandLine::ForCurrentProcess();
 
   if (command_line.HasSwitch(cc::switches::kEnableGpuBenchmarking))
-    GpuBenchmarking::Install(frame_);
+    GpuBenchmarking::Install(this);
 
   if (command_line.HasSwitch(switches::kEnableSkiaBenchmarking))
     SkiaBenchmarking::Install(frame_);
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 7395813..35707b7 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -213,6 +213,13 @@
   CHECK(result.second) << "Inserted a duplicate item.";
 }
 
+void RenderFrameProxy::ResendFrameRects() {
+  // Reset |frame_rect_| in order to allocate a new viz::LocalSurfaceId.
+  gfx::Rect rect = frame_rect_;
+  frame_rect_ = gfx::Rect();
+  FrameRectsChanged(rect);
+}
+
 void RenderFrameProxy::WillBeginCompositorFrame() {
   if (compositing_helper_) {
     FrameHostMsg_HittestData_Params params;
@@ -283,6 +290,7 @@
     IPC_MESSAGE_HANDLER(FrameMsg_ChildFrameProcessGone, OnChildFrameProcessGone)
     IPC_MESSAGE_HANDLER(FrameMsg_SetChildFrameSurface, OnSetChildFrameSurface)
     IPC_MESSAGE_HANDLER(FrameMsg_UpdateOpener, OnUpdateOpener)
+    IPC_MESSAGE_HANDLER(FrameMsg_ViewChanged, OnViewChanged)
     IPC_MESSAGE_HANDLER(FrameMsg_DidStartLoading, OnDidStartLoading)
     IPC_MESSAGE_HANDLER(FrameMsg_DidStopLoading, OnDidStopLoading)
     IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateFramePolicy, OnDidUpdateFramePolicy)
@@ -338,7 +346,11 @@
     compositing_helper_ =
         ChildFrameCompositingHelper::CreateForRenderFrameProxy(this);
   }
-  compositing_helper_->OnSetSurface(surface_info, sequence);
+  // TODO(fsamuel): When surface synchronization is enabled, only set the
+  // fallback here. The primary should be updated on resize/device scale factor
+  // change.
+  compositing_helper_->SetPrimarySurfaceInfo(surface_info);
+  compositing_helper_->SetFallbackSurfaceInfo(surface_info, sequence);
 }
 
 void RenderFrameProxy::OnUpdateOpener(int opener_routing_id) {
@@ -350,6 +362,12 @@
   web_frame_->DidStartLoading();
 }
 
+void RenderFrameProxy::OnViewChanged() {
+  // Resend the FrameRects and allocate a new viz::LocalSurfaceId when the view
+  // changes.
+  ResendFrameRects();
+}
+
 void RenderFrameProxy::OnDidStopLoading() {
   web_frame_->DidStopLoading();
 }
@@ -503,11 +521,17 @@
 
 void RenderFrameProxy::FrameRectsChanged(const blink::WebRect& frame_rect) {
   gfx::Rect rect = frame_rect;
+  if (frame_rect_.size() != rect.size() || !local_surface_id_.is_valid())
+    local_surface_id_ = local_surface_id_allocator_.GenerateId();
+
+  frame_rect_ = rect;
+
   if (IsUseZoomForDSFEnabled()) {
     rect = gfx::ScaleToEnclosingRect(
         rect, 1.f / render_widget_->GetOriginalDeviceScaleFactor());
   }
-  Send(new FrameHostMsg_FrameRectChanged(routing_id_, rect));
+
+  Send(new FrameHostMsg_FrameRectChanged(routing_id_, rect, local_surface_id_));
 }
 
 void RenderFrameProxy::UpdateRemoteViewportIntersection(
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index cbde0285..a471d36c 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
 #include "content/common/content_export.h"
 #include "content/common/feature_policy/feature_policy.h"
 #include "ipc/ipc_listener.h"
@@ -156,6 +157,8 @@
             RenderViewImpl* render_view,
             RenderWidget* render_widget);
 
+  void ResendFrameRects();
+
   // IPC::Listener
   bool OnMessageReceived(const IPC::Message& msg) override;
 
@@ -166,6 +169,7 @@
   void OnSetChildFrameSurface(const viz::SurfaceInfo& surface_info,
                               const viz::SurfaceSequence& sequence);
   void OnUpdateOpener(int opener_routing_id);
+  void OnViewChanged();
   void OnDidStopLoading();
   void OnDidUpdateFramePolicy(
       blink::WebSandboxFlags flags,
@@ -200,6 +204,10 @@
   RenderViewImpl* render_view_;
   RenderWidget* render_widget_;
 
+  gfx::Rect frame_rect_;
+  viz::LocalSurfaceId local_surface_id_;
+  viz::LocalSurfaceIdAllocator local_surface_id_allocator_;
+
   DISALLOW_COPY_AND_ASSIGN(RenderFrameProxy);
 };
 
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index f9ac0e9..fe31bc9 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1018,6 +1018,9 @@
   WebRuntimeFeatures::EnableNewRemotePlaybackPipeline(
       base::FeatureList::IsEnabled(media::kNewRemotePlaybackPipeline));
 
+  WebRuntimeFeatures::EnablePreloadDefaultIsMetadata(
+      base::FeatureList::IsEnabled(media::kPreloadDefaultIsMetadata));
+
   settings->SetPresentationReceiver(prefs.presentation_receiver);
 
   settings->SetMediaControlsEnabled(prefs.media_controls_enabled);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 7b18efc..2ac4221 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -32,7 +32,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/drag_event_source_info.h"
 #include "content/common/drag_messages.h"
-#include "content/common/input/synthetic_gesture_packet.h"
 #include "content/common/input_messages.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/common/swapped_out_messages.h"
@@ -628,8 +627,6 @@
     IPC_MESSAGE_HANDLER(InputMsg_SetEditCommandsForNextKeyEvent,
                         OnSetEditCommandsForNextKeyEvent)
     IPC_MESSAGE_HANDLER(InputMsg_SetFocus, OnSetFocus)
-    IPC_MESSAGE_HANDLER(InputMsg_SyntheticGestureCompleted,
-                        OnSyntheticGestureCompleted)
     IPC_MESSAGE_HANDLER(ViewMsg_ShowContextMenu, OnShowContextMenu)
     IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose)
     IPC_MESSAGE_HANDLER(ViewMsg_Resize, OnResize)
@@ -1551,19 +1548,6 @@
       FROM_HERE, base::Bind(&RenderWidget::DoDeferredClose, this));
 }
 
-void RenderWidget::QueueSyntheticGesture(
-    std::unique_ptr<SyntheticGestureParams> gesture_params,
-    const SyntheticGestureCompletionCallback& callback) {
-  DCHECK(!callback.is_null());
-
-  pending_synthetic_gesture_callbacks_.push(callback);
-
-  SyntheticGesturePacket gesture_packet;
-  gesture_packet.set_gesture_params(std::move(gesture_params));
-
-  Send(new InputHostMsg_QueueSyntheticGesture(routing_id_, gesture_packet));
-}
-
 void RenderWidget::Close() {
   screen_metrics_emulator_.reset();
   WillCloseLayerTreeView();
@@ -1778,13 +1762,6 @@
     compositor_->SetNeedsRedrawRect(gfx::Rect(size_to_paint));
 }
 
-void RenderWidget::OnSyntheticGestureCompleted() {
-  DCHECK(!pending_synthetic_gesture_callbacks_.empty());
-
-  pending_synthetic_gesture_callbacks_.front().Run();
-  pending_synthetic_gesture_callbacks_.pop();
-}
-
 void RenderWidget::OnSetTextDirection(WebTextDirection direction) {
   if (!GetWebWidget())
     return;
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 5e26b77..31b94150 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -30,7 +30,6 @@
 #include "content/common/drag_event_source_info.h"
 #include "content/common/edit_command.h"
 #include "content/common/features.h"
-#include "content/common/input/synthetic_gesture_params.h"
 #include "content/common/widget.mojom.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/common/screen_info.h"
@@ -350,15 +349,6 @@
 
   void SetHandlingInputEventForTesting(bool handling_input_event);
 
-  // Callback for use with synthetic gestures (e.g. BeginSmoothScroll).
-  typedef base::Callback<void()> SyntheticGestureCompletionCallback;
-
-  // Send a synthetic gesture to the browser to be queued to the synthetic
-  // gesture controller.
-  void QueueSyntheticGesture(
-      std::unique_ptr<SyntheticGestureParams> gesture_params,
-      const SyntheticGestureCompletionCallback& callback);
-
   // Deliveres |message| together with compositor state change updates. The
   // exact behavior depends on |policy|.
   // This mechanism is not a drop-in replacement for IPC: messages sent this way
@@ -548,7 +538,6 @@
   virtual void OnDeviceScaleFactorChanged();
 
   void OnRepaint(gfx::Size size_to_paint);
-  void OnSyntheticGestureCompleted();
   void OnSetTextDirection(blink::WebTextDirection direction);
   void OnGetFPS();
   void OnUpdateScreenRects(const gfx::Rect& view_screen_rect,
@@ -790,12 +779,6 @@
   // |screen_info_| on some platforms, and defaults to 1 on other platforms.
   float device_scale_factor_;
 
-  // State associated with synthetic gestures. Synthetic gestures are processed
-  // in-order, so a queue is sufficient to identify the correct state for a
-  // completed gesture.
-  std::queue<SyntheticGestureCompletionCallback>
-      pending_synthetic_gesture_callbacks_;
-
   // True if the IME requests updated composition info.
   bool monitor_composition_info_;
 
diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc
index 6c3847a..5759285 100644
--- a/content/zygote/zygote_main_linux.cc
+++ b/content/zygote/zygote_main_linux.cc
@@ -477,9 +477,11 @@
 
   ZygotePreSandboxInit();
 
-  // Check that the pre-sandbox initialization didn't spawn threads.
+// Check that the pre-sandbox initialization didn't spawn threads.
+// It's not just our code which may do so - some system-installed libraries
+// are known to be culprits, e.g. lttng.
 #if !defined(THREAD_SANITIZER)
-  DCHECK(sandbox::ThreadHelpers::IsSingleThreaded());
+  CHECK(sandbox::ThreadHelpers::IsSingleThreaded());
 #endif
 
   sandbox::SetuidSandboxClient* setuid_sandbox =
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index a056533..59dcbc9 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -151,9 +151,15 @@
   if (device && state == private_api::DEVICE_STATE_TYPE_ENABLED)
     properties->scanning.reset(new bool(device->scanning()));
   if (device && type == ::onc::network_config::kCellular) {
-    properties->sim_present.reset(new bool(!device->IsSimAbsent()));
-    if (!device->sim_lock_type().empty())
-      properties->sim_lock_type.reset(new std::string(device->sim_lock_type()));
+    bool sim_present = !device->IsSimAbsent();
+    properties->sim_present = std::make_unique<bool>(sim_present);
+    if (sim_present) {
+      auto sim_lock_status = base::MakeUnique<private_api::SIMLockStatus>();
+      sim_lock_status->lock_enabled = device->sim_lock_enabled();
+      sim_lock_status->lock_type = device->sim_lock_type();
+      sim_lock_status->retries_left.reset(new int(device->sim_retries_left()));
+      properties->sim_lock_status = std::move(sim_lock_status);
+    }
   }
   device_state_list->push_back(std::move(properties));
 }
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index e8bb112..5bf78370 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -303,7 +303,7 @@
   // process. We use a filter here so that only event listeners for a particular
   // <webview> will fire.
   if (is_web_view_guest) {
-    event_filtering_info.instance_id = is_web_view_guest;
+    event_filtering_info.instance_id = web_view_instance_id;
     histogram_value = events::WEB_VIEW_INTERNAL_ON_MESSAGE;
     event_name = webview::kEventMessage;
   } else {
diff --git a/extensions/common/api/bluetooth_private.idl b/extensions/common/api/bluetooth_private.idl
index b2d760b9..f1b56c6 100644
--- a/extensions/common/api/bluetooth_private.idl
+++ b/extensions/common/api/bluetooth_private.idl
@@ -79,6 +79,9 @@
     DOMString? name;
 
     // Whether or not the adapter has power.
+    // Setting the bluetooth power by setting this property is not recommended,
+    // instead a user pref (ash::prefs::kUserBluetoothAdapterEnabled) should be
+    // set.
     boolean? powered;
 
     // Whether the adapter is discoverable by other devices.
diff --git a/extensions/common/api/networking_onc.idl b/extensions/common/api/networking_onc.idl
index cee468c..024d4d4 100644
--- a/extensions/common/api/networking_onc.idl
+++ b/extensions/common/api/networking_onc.idl
@@ -818,12 +818,11 @@
     // Set if the device is enabled. True if the device is currently scanning.
     boolean? Scanning;
 
-    // Set to the SIM lock type if the device type is Cellular and the device
-    // is locked.
-    DOMString? SimLockType;
+    // The SIM lock status if Type = Cellular and SIMPresent = True.
+    SIMLockStatus? SIMLockStatus;
 
     // Set to the SIM present state if the device type is Cellular.
-    boolean? SimPresent;
+    boolean? SIMPresent;
 
     // The current state of the device.
     DeviceStateType State;
diff --git a/extensions/common/api/networking_private.idl b/extensions/common/api/networking_private.idl
index 2ec75df..00afb36 100644
--- a/extensions/common/api/networking_private.idl
+++ b/extensions/common/api/networking_private.idl
@@ -761,12 +761,11 @@
     // Set if the device is enabled. True if the device is currently scanning.
     boolean? Scanning;
 
-    // Set to the SIM lock type if the device type is Cellular and the device
-    // is locked.
-    DOMString? SimLockType;
+    // The SIM lock status if Type = Cellular and SIMPresent = True.
+    SIMLockStatus? SIMLockStatus;
 
     // Set to the SIM present state if the device type is Cellular.
-    boolean? SimPresent;
+    boolean? SIMPresent;
 
     // The current state of the device.
     DeviceStateType State;
diff --git a/extensions/renderer/extension_bindings_system.cc b/extensions/renderer/extension_bindings_system.cc
index ffee346..c7fd2d5 100644
--- a/extensions/renderer/extension_bindings_system.cc
+++ b/extensions/renderer/extension_bindings_system.cc
@@ -4,6 +4,7 @@
 
 #include "extensions/renderer/extension_bindings_system.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/externally_connectable.h"
 #include "extensions/renderer/renderer_extension_registry.h"
@@ -11,6 +12,12 @@
 
 namespace extensions {
 
+namespace {
+
+const int kHistogramBucketCount = 50;
+
+}  // namespace
+
 // static
 bool ExtensionBindingsSystem::IsRuntimeAvailableToContext(
     ScriptContext* context) {
@@ -29,4 +36,67 @@
     "app", "webstore", "dashboardPrivate",
 };
 
+void ExtensionBindingsSystem::LogUpdateBindingsForContextTime(
+    Feature::Context context_type,
+    base::TimeDelta elapsed) {
+  static const int kTenSecondsInMicroseconds = 10000000;
+  switch (context_type) {
+    case Feature::UNSPECIFIED_CONTEXT:
+      break;
+    case Feature::WEB_PAGE_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime.WebPageContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::BLESSED_WEB_PAGE_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "BlessedWebPageContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::SERVICE_WORKER_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "ServiceWorkerContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::BLESSED_EXTENSION_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "BlessedExtensionContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "LockScreenExtensionContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::UNBLESSED_EXTENSION_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "UnblessedExtensionContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::CONTENT_SCRIPT_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime."
+          "ContentScriptContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+      break;
+    case Feature::WEBUI_CONTEXT:
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Extensions.Bindings.UpdateBindingsForContextTime.WebUIContext",
+          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+          kHistogramBucketCount);
+  }
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/extension_bindings_system.h b/extensions/renderer/extension_bindings_system.h
index 6791fcab..d1e0d6ef 100644
--- a/extensions/renderer/extension_bindings_system.h
+++ b/extensions/renderer/extension_bindings_system.h
@@ -7,7 +7,9 @@
 
 #include <string>
 
+#include "base/time/time.h"
 #include "extensions/common/extension_id.h"
+#include "extensions/common/features/feature.h"
 
 namespace base {
 class ListValue;
@@ -76,6 +78,11 @@
   // case of extensions communicating with external websites).
   static bool IsRuntimeAvailableToContext(ScriptContext* context);
 
+  // Logs the amount of time taken to update the bindings for a given context
+  // (i.e., UpdateBindingsForContext()).
+  static void LogUpdateBindingsForContextTime(Feature::Context context_type,
+                                              base::TimeDelta elapsed);
+
   // The APIs that could potentially be available to webpage-like contexts.
   // This is the list of possible features; most web pages will not have access
   // to these APIs.
diff --git a/extensions/renderer/js_extension_bindings_system.cc b/extensions/renderer/js_extension_bindings_system.cc
index 457eb570..dd73b13 100644
--- a/extensions/renderer/js_extension_bindings_system.cc
+++ b/extensions/renderer/js_extension_bindings_system.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_split.h"
+#include "base/timer/elapsed_timer.h"
 #include "content/public/child/v8_value_converter.h"
 #include "content/public/common/content_switches.h"
 #include "extensions/common/extension.h"
@@ -158,6 +159,8 @@
 
 void JsExtensionBindingsSystem::UpdateBindingsForContext(
     ScriptContext* context) {
+  base::ElapsedTimer timer;
+
   v8::HandleScope handle_scope(context->isolate());
   v8::Context::Scope context_scope(context->v8_context());
 
@@ -223,6 +226,8 @@
       break;
     }
   }
+
+  LogUpdateBindingsForContextTime(context->context_type(), timer.Elapsed());
 }
 
 void JsExtensionBindingsSystem::HandleResponse(int request_id,
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index 439ce42b..8ef8369 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -7,6 +7,8 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
 #include "content/public/child/worker_thread.h"
 #include "content/public/common/console_message_level.h"
 #include "content/public/common/content_switches.h"
@@ -452,6 +454,7 @@
 
 void NativeExtensionBindingsSystem::UpdateBindingsForContext(
     ScriptContext* context) {
+  base::ElapsedTimer timer;
   v8::Isolate* isolate = context->isolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> v8_context = context->v8_context();
@@ -509,6 +512,7 @@
     if (IsRuntimeAvailableToContext(context) && !set_accessor("runtime"))
       LOG(ERROR) << "Failed to create API on Chrome object.";
 
+    LogUpdateBindingsForContextTime(context->context_type(), timer.Elapsed());
     return;
   }
 
@@ -540,6 +544,8 @@
       return;
     }
   }
+
+  LogUpdateBindingsForContextTime(context->context_type(), timer.Elapsed());
 }
 
 void NativeExtensionBindingsSystem::DispatchEventInContext(
@@ -647,6 +653,7 @@
   CHECK(
       gin::Converter<std::string>::FromV8(isolate, api_name, &api_name_string));
 
+  base::ElapsedTimer timer;
   v8::Local<v8::Object> root_binding = CreateFullBinding(
       context, script_context, &data->bindings_system->api_system_,
       FeatureProvider::GetAPIFeatures(), api_name_string);
@@ -658,6 +665,9 @@
   if (!success.IsJust() || !success.FromJust())
     return v8::Local<v8::Object>();
 
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.Bindings.NativeBindingCreationTime",
+                              timer.Elapsed().InMicroseconds(), 1, 10000000,
+                              50);
   return root_binding;
 }
 
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn
index 4f71d1c..83decf6b 100644
--- a/ios/chrome/browser/ui/history/BUILD.gn
+++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -15,8 +15,6 @@
     "history_collection_view_controller.mm",
     "history_entries_status_item.h",
     "history_entries_status_item.mm",
-    "history_entry.cc",
-    "history_entry.h",
     "history_entry_inserter.h",
     "history_entry_inserter.mm",
     "history_entry_item.h",
@@ -27,11 +25,10 @@
     "history_search_view.mm",
     "history_search_view_controller.h",
     "history_search_view_controller.mm",
-    "history_service_facade.h",
-    "history_service_facade.mm",
-    "history_service_facade_delegate.h",
     "history_util.h",
     "history_util.mm",
+    "ios_browsing_history_driver.h",
+    "ios_browsing_history_driver.mm",
   ]
   deps = [
     "//base",
@@ -93,8 +90,6 @@
     "history_entry_inserter_unittest.mm",
     "history_entry_item_unittest.mm",
     "history_search_view_controller_unittest.mm",
-    "history_service_facade_unittest.mm",
-    "history_util_unittest.mm",
   ]
   deps = [
     ":history",
diff --git a/ios/chrome/browser/ui/history/history_collection_view_controller.mm b/ios/chrome/browser/ui/history/history_collection_view_controller.mm
index 9d5f9ebd..fd8c1fe 100644
--- a/ios/chrome/browser/ui/history/history_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_collection_view_controller.mm
@@ -13,13 +13,19 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/browser_sync/profile_sync_service.h"
 #include "components/browsing_data/core/history_notice_utils.h"
+#include "components/history/core/browser/browsing_history_driver.h"
+#include "components/history/core/browser/browsing_history_service.h"
+#include "components/keyed_service/core/service_access_type.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/url_formatter.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
+#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
@@ -29,12 +35,10 @@
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h"
 #include "ios/chrome/browser/ui/history/history_entries_status_item.h"
-#include "ios/chrome/browser/ui/history/history_entry.h"
 #include "ios/chrome/browser/ui/history/history_entry_inserter.h"
 #import "ios/chrome/browser/ui/history/history_entry_item.h"
-#include "ios/chrome/browser/ui/history/history_service_facade.h"
-#include "ios/chrome/browser/ui/history/history_service_facade_delegate.h"
 #include "ios/chrome/browser/ui/history/history_util.h"
+#include "ios/chrome/browser/ui/history/ios_browsing_history_driver.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -51,6 +55,8 @@
 #error "This file requires ARC support."
 #endif
 
+using history::BrowsingHistoryService;
+
 namespace {
 typedef NS_ENUM(NSInteger, ItemType) {
   ItemTypeHistoryEntry = kItemTypeEnumZero,
@@ -68,9 +74,11 @@
 @interface HistoryCollectionViewController ()<HistoryEntriesStatusItemDelegate,
                                               HistoryEntryInserterDelegate,
                                               HistoryEntryItemDelegate,
-                                              HistoryServiceFacadeDelegate> {
-  // Facade for communicating with HistoryService and WebHistoryService.
-  std::unique_ptr<HistoryServiceFacade> _historyServiceFacade;
+                                              BrowsingHistoryDriverDelegate> {
+  // Abstraction to communicate with HistoryService and WebHistoryService.
+  std::unique_ptr<BrowsingHistoryService> _browsingHistoryService;
+  // Provides dependencies and funnels callbacks from BrowsingHistoryService.
+  std::unique_ptr<IOSBrowsingHistoryDriver> _browsingHistoryDriver;
   // The main browser state. Not owned by HistoryCollectionViewController.
   ios::ChromeBrowserState* _browserState;
   // Backing ivar for delegate property.
@@ -151,7 +159,13 @@
   self =
       [super initWithLayout:layout style:CollectionViewControllerStyleDefault];
   if (self) {
-    _historyServiceFacade.reset(new HistoryServiceFacade(browserState, self));
+    _browsingHistoryDriver =
+        std::make_unique<IOSBrowsingHistoryDriver>(browserState, self);
+    _browsingHistoryService = std::make_unique<BrowsingHistoryService>(
+        _browsingHistoryDriver.get(),
+        ios::HistoryServiceFactory::GetForBrowserState(
+            browserState, ServiceAccessType::EXPLICIT_ACCESS),
+        IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState));
     _browserState = browserState;
     _delegate = delegate;
     _URLLoader = loader;
@@ -215,14 +229,16 @@
 
 - (void)deleteSelectedItemsFromHistory {
   NSArray* deletedIndexPaths = self.collectionView.indexPathsForSelectedItems;
-  std::vector<HistoryServiceFacade::RemovedEntry> entries;
+  std::vector<BrowsingHistoryService::HistoryEntry> entries;
   for (NSIndexPath* indexPath in deletedIndexPaths) {
     HistoryEntryItem* object = base::mac::ObjCCastStrict<HistoryEntryItem>(
         [self.collectionViewModel itemAtIndexPath:indexPath]);
-    entries.push_back(
-        HistoryServiceFacade::RemovedEntry(object.URL, object.timestamp));
+    BrowsingHistoryService::HistoryEntry entry;
+    entry.url = object.URL;
+    entry.all_timestamps.insert(object.timestamp.ToInternalValue());
+    entries.push_back(entry);
   }
-  _historyServiceFacade->RemoveHistoryEntries(entries);
+  _browsingHistoryService->RemoveVisits(entries);
   [self removeSelectedItemsFromCollection];
 }
 
@@ -315,47 +331,52 @@
   }
 }
 
-#pragma mark - HistoryServiceFacadeDelegate
+#pragma mark - BrowsingHistoryDriverDelegate
 
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-       didReceiveQueryResult:(HistoryServiceFacade::QueryResult)result {
+- (void)onQueryCompleteWithResults:
+            (const std::vector<BrowsingHistoryService::HistoryEntry>&)results
+                  queryResultsInfo:
+                      (const BrowsingHistoryService::QueryResultsInfo&)
+                          queryResultsInfo {
   self.loading = NO;
+
   // If history sync is enabled and there hasn't been a response from synced
   // history, try fetching again.
   SyncSetupService* syncSetupService =
       SyncSetupServiceFactory::GetForBrowserState(_browserState);
   if (syncSetupService->IsSyncEnabled() &&
       syncSetupService->IsDataTypeEnabled(syncer::HISTORY_DELETE_DIRECTIVES) &&
-      !result.sync_returned) {
+      queryResultsInfo.sync_timed_out) {
     [self showHistoryMatchingQuery:_currentQuery];
     return;
   }
 
   // If there are no results and no URLs have been loaded, report that no
   // history entries were found.
-  if (result.entries.empty() && !self.hasHistoryEntries) {
+  if (results.empty() && !self.hasHistoryEntries) {
     DCHECK(self.entriesType == NO_ENTRIES);
     [self updateEntriesStatusMessage];
     [self.delegate historyCollectionViewControllerDidChangeEntries:self];
     return;
   }
 
-  self.finishedLoading = result.has_synced_results
-                             ? result.finished && result.sync_finished
-                             : result.finished;
-  self.entriesType = result.has_synced_results ? SYNCED_ENTRIES : LOCAL_ENTRIES;
-  std::vector<history::HistoryEntry> entries = result.entries;
+  self.finishedLoading = queryResultsInfo.reached_beginning_of_local &&
+                         (!queryResultsInfo.has_synced_results ||
+                          queryResultsInfo.reached_beginning_of_sync);
+  self.entriesType =
+      queryResultsInfo.has_synced_results ? SYNCED_ENTRIES : LOCAL_ENTRIES;
 
   // Header section should be updated outside of batch updates, otherwise
   // loading indicator removal will not be observed.
   [self updateEntriesStatusMessage];
 
-  __block NSMutableArray* filterResults = [NSMutableArray array];
-  __block NSString* searchQuery = [base::SysUTF16ToNSString(result.query) copy];
+  NSMutableArray* filterResults = [NSMutableArray array];
+  NSString* searchQuery =
+      [base::SysUTF16ToNSString(queryResultsInfo.search_text) copy];
   [self.collectionView performBatchUpdates:^{
     // There should always be at least a header section present.
     DCHECK([[self collectionViewModel] numberOfSections]);
-    for (const history::HistoryEntry& entry : entries) {
+    for (const BrowsingHistoryService::HistoryEntry& entry : results) {
       HistoryEntryItem* item =
           [[HistoryEntryItem alloc] initWithType:ItemTypeHistoryEntry
                                     historyEntry:entry
@@ -372,16 +393,16 @@
         if (([self isSearching] && [searchQuery length] > 0 &&
              [self.currentQuery isEqualToString:searchQuery]) ||
             self.filterQueryResult) {
-          // If in search mode, filter out entries that are not
-          // part of the search result.
+          // If in search mode, filter out entries that are not part of the
+          // search result.
           [self filterForHistoryEntries:filterResults];
           self.filterQueryResult = NO;
         }
       }];
 }
 
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-    shouldShowNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)shouldShowNotice {
+- (void)shouldShowNoticeAboutOtherFormsOfBrowsingHistory:
+    (BOOL)shouldShowNotice {
   self.shouldShowNoticeAboutOtherFormsOfBrowsingHistory = shouldShowNotice;
   // Update the history entries status message if there is no query in progress.
   if (!self.isLoading) {
@@ -389,8 +410,7 @@
   }
 }
 
-- (void)historyServiceFacadeDidObserveHistoryDeletion:
-    (HistoryServiceFacade*)facade {
+- (void)didObserverHistoryDeletion {
   // If history has been deleted, reload history filtering for the current
   // results. This only observes local changes to history, i.e. removing
   // history via the clear browsing data page.
@@ -555,10 +575,7 @@
   options.max_count = kMaxFetchCount;
   options.matching_algorithm =
       query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH;
-  _historyServiceFacade->QueryHistory(queryString, options);
-  // Also determine whether notice regarding other forms of browsing history
-  // should be shown.
-  _historyServiceFacade->QueryOtherFormsOfBrowsingHistory();
+  _browsingHistoryService->QueryHistory(queryString, options);
 }
 
 - (void)updateEntriesStatusMessage {
diff --git a/ios/chrome/browser/ui/history/history_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/history/history_collection_view_controller_unittest.mm
index 8454850..3b264ce 100644
--- a/ios/chrome/browser/ui/history/history_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/history/history_collection_view_controller_unittest.mm
@@ -10,6 +10,7 @@
 #include "base/strings/string16.h"
 #import "base/test/ios/wait_util.h"
 #include "base/time/time.h"
+#include "components/history/core/browser/browsing_history_service.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/authentication_service_fake.h"
@@ -18,9 +19,7 @@
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_mock.h"
-#import "ios/chrome/browser/ui/history/history_entry.h"
-#import "ios/chrome/browser/ui/history/history_service_facade.h"
-#import "ios/chrome/browser/ui/history/history_service_facade_delegate.h"
+#import "ios/chrome/browser/ui/history/ios_browsing_history_driver.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #include "ios/chrome/test/block_cleanup_test.h"
 #include "ios/web/public/test/test_web_thread.h"
@@ -29,21 +28,25 @@
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
 
+using base::Time;
+using base::TimeDelta;
+using history::BrowsingHistoryService;
+
 namespace {
 
-HistoryServiceFacade::QueryResult QueryResultWithVisits(
-    std::vector<std::pair<const GURL&, base::Time>> visits) {
-  std::vector<history::HistoryEntry> entries{};
-  for (std::pair<const GURL&, base::Time> visit : visits) {
-    history::HistoryEntry entry = history::HistoryEntry();
+const char kTestUrl1[] = "http://test1/";
+const char kTestUrl2[] = "http://test2/";
+
+std::vector<BrowsingHistoryService::HistoryEntry> QueryResultWithVisits(
+    std::vector<std::pair<const GURL&, Time>> visits) {
+  std::vector<BrowsingHistoryService::HistoryEntry> entries;
+  for (std::pair<const GURL&, Time> visit : visits) {
+    BrowsingHistoryService::HistoryEntry entry;
     entry.url = visit.first;
     entry.time = visit.second;
     entries.push_back(entry);
   }
-  HistoryServiceFacade::QueryResult result{};
-  result.entries = entries;
-  result.finished = true;
-  return result;
+  return entries;
 }
 
 std::unique_ptr<KeyedService> BuildMockSyncSetupService(
@@ -59,7 +62,7 @@
 }  // namespace
 
 @interface HistoryCollectionViewController (
-    Testing)<HistoryServiceFacadeDelegate>
+    Testing)<BrowsingHistoryDriverDelegate>
 - (void)didPressClearBrowsingBar;
 @end
 
@@ -95,6 +98,16 @@
     BlockCleanupTest::TearDown();
   }
 
+  void QueryHistory(std::vector<std::pair<const GURL&, Time>> visits) {
+    std::vector<BrowsingHistoryService::HistoryEntry> results =
+        QueryResultWithVisits(visits);
+    BrowsingHistoryService::QueryResultsInfo query_results_info;
+    query_results_info.reached_beginning_of_local = true;
+    [history_collection_view_controller_
+        onQueryCompleteWithResults:results
+                  queryResultsInfo:query_results_info];
+  }
+
  protected:
   web::TestWebThreadBundle thread_bundle_;
   id<UrlLoader> mock_url_loader_;
@@ -109,11 +122,7 @@
 // Tests that hasHistoryEntries property returns YES after entries have been
 // received.
 TEST_F(HistoryCollectionViewControllerTest, HasHistoryEntries) {
-  GURL url_1("http://test1");
-  HistoryServiceFacade::QueryResult query_result =
-      QueryResultWithVisits({{url_1, base::Time::Now()}});
-  [history_collection_view_controller_ historyServiceFacade:nil
-                                      didReceiveQueryResult:query_result];
+  QueryHistory({{GURL(kTestUrl1), Time::Now()}});
   EXPECT_TRUE([history_collection_view_controller_ hasHistoryEntries]);
 }
 
@@ -122,30 +131,21 @@
 // This ensures that when HISTORY_DELETE_DIRECTIVES is disabled,
 // only local device history items are shown.
 TEST_F(HistoryCollectionViewControllerTest, HasHistoryEntriesWhenSyncEnabled) {
-  GURL url_1("http://test1");
   EXPECT_CALL(*sync_setup_service_mock_, IsSyncEnabled())
       .WillRepeatedly(testing::Return(true));
   EXPECT_CALL(*sync_setup_service_mock_,
               IsDataTypeEnabled(syncer::HISTORY_DELETE_DIRECTIVES))
       .WillRepeatedly(testing::Return(false));
 
-  HistoryServiceFacade::QueryResult query_result =
-      QueryResultWithVisits({{url_1, base::Time::Now()}});
-  [history_collection_view_controller_ historyServiceFacade:nil
-                                      didReceiveQueryResult:query_result];
+  QueryHistory({{GURL(kTestUrl1), Time::Now()}});
   EXPECT_TRUE([history_collection_view_controller_ hasHistoryEntries]);
 }
 
 // Tests adding two entries to history from the same day, then deleting the
 // first of them results in one history entry in the collection.
 TEST_F(HistoryCollectionViewControllerTest, DeleteSingleEntry) {
-  // Add history entries
-  GURL url_1("http://test1");
-  GURL url_2("http://test2");
-  HistoryServiceFacade::QueryResult query_result = QueryResultWithVisits(
-      {{url_1, base::Time::Now()}, {url_2, base::Time::Now()}});
-  [history_collection_view_controller_ historyServiceFacade:nil
-                                      didReceiveQueryResult:query_result];
+  QueryHistory(
+      {{GURL(kTestUrl1), Time::Now()}, {GURL(kTestUrl2), Time::Now()}});
 
   UICollectionView* collection_view =
       [history_collection_view_controller_ collectionView];
@@ -164,13 +164,8 @@
 // Tests that adding two entries to history from the same day then deleting
 // both of them results in only the header section in the collection.
 TEST_F(HistoryCollectionViewControllerTest, DeleteMultipleEntries) {
-  // Add history entries.
-  GURL url_1("http://test1");
-  GURL url_2("http://test2");
-  HistoryServiceFacade::QueryResult query_result = QueryResultWithVisits(
-      {{url_1, base::Time::Now()}, {url_2, base::Time::Now()}});
-  [history_collection_view_controller_ historyServiceFacade:nil
-                                      didReceiveQueryResult:query_result];
+  QueryHistory(
+      {{GURL(kTestUrl1), Time::Now()}, {GURL(kTestUrl2), Time::Now()}});
 
   // Select history entries and tap delete.
   UICollectionView* collection_view =
@@ -195,14 +190,9 @@
 // Tests that adding two entries to history from different days then deleting
 // both of them results in only the header section in the collection.
 TEST_F(HistoryCollectionViewControllerTest, DeleteMultipleSections) {
-  GURL url_1("http://test1");
-  GURL url_2("http://test2");
+  QueryHistory({{GURL(kTestUrl1), Time::Now() - TimeDelta::FromDays(1)},
+                {GURL(kTestUrl2), Time::Now()}});
 
-  HistoryServiceFacade::QueryResult query_result = QueryResultWithVisits(
-      {{url_1, base::Time::Now() - base::TimeDelta::FromDays(1)},
-       {url_2, base::Time::Now()}});
-  [history_collection_view_controller_ historyServiceFacade:nil
-                                      didReceiveQueryResult:query_result];
   UICollectionView* collection_view =
       [history_collection_view_controller_ collectionView];
   // Expect two history sections in addition to the header section.
diff --git a/ios/chrome/browser/ui/history/history_entry.cc b/ios/chrome/browser/ui/history/history_entry.cc
deleted file mode 100644
index 32e8f0a..0000000
--- a/ios/chrome/browser/ui/history/history_entry.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ui/history/history_entry.h"
-
-namespace history {
-
-HistoryEntry::HistoryEntry(HistoryEntry::EntryType entry_type,
-                           const GURL& url,
-                           const base::string16& title,
-                           base::Time time,
-                           const std::string& client_id,
-                           bool is_search_result,
-                           const base::string16& snippet,
-                           bool blocked_visit)
-    : entry_type(entry_type),
-      url(url),
-      title(title),
-      time(time),
-      client_id(client_id),
-      is_search_result(is_search_result),
-      snippet(snippet),
-      blocked_visit(blocked_visit) {
-  all_timestamps.insert(time.ToInternalValue());
-}
-
-HistoryEntry::HistoryEntry()
-    : entry_type(EMPTY_ENTRY), is_search_result(false), blocked_visit(false) {}
-
-HistoryEntry::HistoryEntry(const HistoryEntry& other) = default;
-
-HistoryEntry::~HistoryEntry() {}
-
-bool HistoryEntry::SortByTimeDescending(const HistoryEntry& entry1,
-                                        const HistoryEntry& entry2) {
-  return entry1.time > entry2.time;
-}
-
-}  // namespace history
diff --git a/ios/chrome/browser/ui/history/history_entry.h b/ios/chrome/browser/ui/history/history_entry.h
deleted file mode 100644
index 715900f..0000000
--- a/ios/chrome/browser/ui/history/history_entry.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_ENTRY_H_
-#define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_ENTRY_H_
-
-#include <set>
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "url/gurl.h"
-
-namespace history {
-
-// Represents a history entry to be shown to the user, representing either
-// a local or remote visit. A single entry can represent multiple visits,
-// since only the most recent visit on a particular day is shown.
-struct HistoryEntry {
-  // Values indicating whether an entry represents only local visits, only
-  // remote visits, or a mixture of both.
-  enum EntryType { EMPTY_ENTRY = 0, LOCAL_ENTRY, REMOTE_ENTRY, COMBINED_ENTRY };
-
-  HistoryEntry(EntryType type,
-               const GURL& url,
-               const base::string16& title,
-               base::Time time,
-               const std::string& client_id,
-               bool is_search_result,
-               const base::string16& snippet,
-               bool blocked_visit);
-  HistoryEntry();
-  HistoryEntry(const HistoryEntry&);
-  ~HistoryEntry();
-
-  // Comparison function for sorting HistoryEntries from newest to oldest.
-  static bool SortByTimeDescending(const HistoryEntry& entry1,
-                                   const HistoryEntry& entry2);
-
-  // The type of visits this entry represents: local, remote, or both.
-  EntryType entry_type;
-
-  // URL of the entry.
-  GURL url;
-
-  // Title of the entry. May be empty.
-  base::string16 title;
-
-  // Time of the entry. Usually this will be the time of the most recent
-  // visit to |url| on a particular day as defined in the local timezone.
-  base::Time time;
-
-  // Sync ID of the client on which the most recent visit occurred.
-  std::string client_id;
-
-  // Timestamps of all local or remote visits to the same URL on the same day.
-  std::set<int64_t> all_timestamps;
-
-  // If true, this entry is a history query result.
-  bool is_search_result;
-
-  // The entry's search snippet, if this entry is a history query result.
-  base::string16 snippet;
-
-  // Whether this entry was blocked when it was attempted.
-  bool blocked_visit;
-};
-}  // namespace history
-
-#endif  // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_ENTRY_H_
diff --git a/ios/chrome/browser/ui/history/history_entry_inserter_unittest.mm b/ios/chrome/browser/ui/history/history_entry_inserter_unittest.mm
index f85a6b3..0df59900 100644
--- a/ios/chrome/browser/ui/history/history_entry_inserter_unittest.mm
+++ b/ios/chrome/browser/ui/history/history_entry_inserter_unittest.mm
@@ -7,8 +7,8 @@
 #import "base/mac/foundation_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "components/history/core/browser/browsing_history_service.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
-#import "ios/chrome/browser/ui/history/history_entry.h"
 #import "ios/chrome/browser/ui/history/history_entry_item.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
@@ -20,12 +20,14 @@
 #error "This file requires ARC support."
 #endif
 
+using history::BrowsingHistoryService;
+
 HistoryEntryItem* TestHistoryEntryItem(base::Time timestamp,
                                        const std::string& name) {
-  history::HistoryEntry entry = history::HistoryEntry(
-      history::HistoryEntry::LOCAL_ENTRY, GURL(("http://" + name).c_str()),
-      base::UTF8ToUTF16(name.c_str()), timestamp, std::string(), false,
-      base::string16(), false);
+  BrowsingHistoryService::HistoryEntry entry(
+      BrowsingHistoryService::HistoryEntry::LOCAL_ENTRY,
+      GURL(("http://" + name).c_str()), base::UTF8ToUTF16(name.c_str()),
+      timestamp, std::string(), false, base::string16(), false);
   return [[HistoryEntryItem alloc] initWithType:kItemTypeEnumZero
                                    historyEntry:entry
                                    browserState:nil
diff --git a/ios/chrome/browser/ui/history/history_entry_item.h b/ios/chrome/browser/ui/history/history_entry_item.h
index 103d0be..367191e 100644
--- a/ios/chrome/browser/ui/history/history_entry_item.h
+++ b/ios/chrome/browser/ui/history/history_entry_item.h
@@ -5,6 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_ENTRY_ITEM_H_
 #define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_ENTRY_ITEM_H_
 
+#include "components/history/core/browser/browsing_history_service.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/third_party/material_components_ios/src/components/Collections/src/MaterialCollections.h"
 
@@ -12,10 +13,6 @@
 class Time;
 }  // namespace base
 
-namespace history {
-struct HistoryEntry;
-}  // namespace history
-
 namespace ios {
 class ChromeBrowserState;
 }  // namespace ios
@@ -59,7 +56,8 @@
 
 // The |delegate| is notified when the favicon has loaded, and may be nil.
 - (instancetype)initWithType:(NSInteger)type
-                historyEntry:(const history::HistoryEntry&)entry
+                historyEntry:
+                    (const history::BrowsingHistoryService::HistoryEntry&)entry
                 browserState:(ios::ChromeBrowserState*)browserState
                     delegate:(id<HistoryEntryItemDelegate>)delegate
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/history/history_entry_item.mm b/ios/chrome/browser/ui/history/history_entry_item.mm
index b917582..f5d15d8 100644
--- a/ios/chrome/browser/ui/history/history_entry_item.mm
+++ b/ios/chrome/browser/ui/history/history_entry_item.mm
@@ -14,7 +14,6 @@
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #import "ios/chrome/browser/ui/history/favicon_view.h"
 #import "ios/chrome/browser/ui/history/favicon_view_provider.h"
-#import "ios/chrome/browser/ui/history/history_entry.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -91,7 +90,8 @@
 @synthesize timestamp = _timestamp;
 
 - (instancetype)initWithType:(NSInteger)type
-                historyEntry:(const history::HistoryEntry&)entry
+                historyEntry:
+                    (const history::BrowsingHistoryService::HistoryEntry&)entry
                 browserState:(ios::ChromeBrowserState*)browserState
                     delegate:(id<HistoryEntryItemDelegate>)delegate {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/history/history_entry_item_unittest.mm b/ios/chrome/browser/ui/history/history_entry_item_unittest.mm
index 9a86ef07..eacffdf1 100644
--- a/ios/chrome/browser/ui/history/history_entry_item_unittest.mm
+++ b/ios/chrome/browser/ui/history/history_entry_item_unittest.mm
@@ -8,7 +8,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "ios/chrome/browser/ui/history/history_entry.h"
+#include "components/history/core/browser/browsing_history_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 
@@ -16,6 +16,8 @@
 #error "This file requires ARC support."
 #endif
 
+using history::BrowsingHistoryService;
+
 namespace {
 const char kTestUrl[] = "http://test/";
 const char kTestUrl2[] = "http://test2/";
@@ -25,9 +27,9 @@
 HistoryEntryItem* GetHistoryEntryItem(const GURL& url,
                                       const char title[],
                                       base::Time timestamp) {
-  history::HistoryEntry entry = history::HistoryEntry(
-      history::HistoryEntry::LOCAL_ENTRY, GURL(url), base::UTF8ToUTF16(title),
-      timestamp, "", false, base::string16(), false);
+  BrowsingHistoryService::HistoryEntry entry(
+      BrowsingHistoryService::HistoryEntry::LOCAL_ENTRY, GURL(url),
+      base::UTF8ToUTF16(title), timestamp, "", false, base::string16(), false);
   HistoryEntryItem* item = [[HistoryEntryItem alloc] initWithType:0
                                                      historyEntry:entry
                                                      browserState:nil
diff --git a/ios/chrome/browser/ui/history/history_service_facade.h b/ios/chrome/browser/ui/history/history_service_facade.h
deleted file mode 100644
index cb037786..0000000
--- a/ios/chrome/browser/ui/history/history_service_facade.h
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_H_
-#define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
-#include "base/strings/string16.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "base/timer/timer.h"
-#include "base/values.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/history/core/browser/url_row.h"
-#include "components/history/core/browser/web_history_service.h"
-#include "url/gurl.h"
-
-namespace history {
-struct HistoryEntry;
-class HistoryService;
-struct QueryOptions;
-class QueryResults;
-}
-
-namespace ios {
-class ChromeBrowserState;
-}
-
-@protocol HistoryServiceFacadeDelegate;
-
-// Facade for HistoryService and WebHistoryService. Handles history querying and
-// deletion actions.
-class HistoryServiceFacade : public history::HistoryServiceObserver {
- public:
-  // Represents the result of a query to history service.
-  struct QueryResult {
-    QueryResult();
-    QueryResult(const QueryResult&);
-    ~QueryResult();
-    base::string16 query;
-    base::string16 query_start_time;
-    base::string16 query_end_time;
-    // true if all local history from History service has been retrieved.
-    bool finished;
-    // true if a query to WebHistoryService has returned successfully.
-    bool sync_returned;
-    // true if results from WebHistoryService have been retrieved.
-    bool has_synced_results;
-    // true if all remote history from WebHistoryService has been retrieved.
-    bool sync_finished;
-    std::vector<history::HistoryEntry> entries;
-  };
-
-  // Represents a history entry removed by the client.
-  struct RemovedEntry {
-    RemovedEntry(const GURL& url, const base::Time& timestamp);
-    RemovedEntry(const GURL& url, const std::vector<base::Time>& timestamps);
-    RemovedEntry(const RemovedEntry&);
-    ~RemovedEntry();
-    GURL url;
-    std::vector<base::Time> timestamps;
-  };
-
-  HistoryServiceFacade(ios::ChromeBrowserState* browser_state,
-                       id<HistoryServiceFacadeDelegate> delegate);
-  ~HistoryServiceFacade() override;
-
-  // Performs history query with query |search_text| and |options|;
-  void QueryHistory(const base::string16& search_text,
-                    const history::QueryOptions& options);
-
-  // Removes history entries in HistoryService and WebHistoryService.
-  void RemoveHistoryEntries(const std::vector<RemovedEntry>& entries);
-
-  // Queries WebHistoryService to determine whether notice about other forms
-  // of browsing history should be shown. The response is returned via the
-  // historyServiceFacade:shouldShowNoticeAboutOtherFormsOfBrowsingHistory:
-  // delegate callback.
-  void QueryOtherFormsOfBrowsingHistory();
-
- private:
-  // The range for which to return results:
-  // - ALLTIME: allows access to all the results in a paginated way.
-  // - WEEK: the last 7 days.
-  // - MONTH: the last calendar month.
-  enum Range { ALL_TIME = 0, WEEK = 1, MONTH = 2 };
-
-  // Callback from |web_history_timer_| when a response from web history has
-  // not been received in time.
-  void WebHistoryTimeout();
-
-  // Callback from the history system when a history query has completed.
-  void QueryComplete(const base::string16& search_text,
-                     const history::QueryOptions& options,
-                     history::QueryResults* results);
-
-  // Callback from the WebHistoryService when a query has completed.
-  void WebHistoryQueryComplete(const base::string16& search_text,
-                               const history::QueryOptions& options,
-                               base::TimeTicks start_time,
-                               history::WebHistoryService::Request* request,
-                               const base::DictionaryValue* results_value);
-
-  // Callback from the history system when visits were deleted.
-  void RemoveComplete();
-
-  // Callback from history server when visits were deleted.
-  void RemoveWebHistoryComplete(bool success);
-
-  // Callback telling  whether other forms of browsing history were found
-  // on the history server.
-  void OtherFormsOfBrowsingHistoryQueryComplete(
-      bool found_other_forms_of_browsing_history);
-
-  // Combines the query results from the local history database and the history
-  // server, and sends the combined results to the front end.
-  void ReturnResultsToFrontEnd();
-
-  // history::HistoryServiceObserver method.
-  void OnURLsDeleted(history::HistoryService* history_service,
-                     bool all_history,
-                     bool expired,
-                     const history::URLRows& deleted_rows,
-                     const std::set<GURL>& favicon_urls) override;
-
-  // Tracker for search requests to the history service.
-  base::CancelableTaskTracker query_task_tracker_;
-
-  // The currently-executing request for synced history results.
-  // Deleting the request will cancel it.
-  std::unique_ptr<history::WebHistoryService::Request> web_history_request_;
-
-  // True if there is a pending delete requests to the history service.
-  bool has_pending_delete_request_;
-
-  // Tracker for delete requests to the history service.
-  base::CancelableTaskTracker delete_task_tracker_;
-
-  // The list of URLs that are in the process of being deleted.
-  std::set<GURL> urls_to_be_deleted_;
-
-  // Information that is returned to the front end with the query results.
-  QueryResult results_info_value_;
-
-  // The list of query results received from the history service.
-  std::vector<history::HistoryEntry> query_results_;
-
-  // The list of query results received from the history server.
-  std::vector<history::HistoryEntry> web_history_query_results_;
-
-  // Timer used to implement a timeout on a Web History response.
-  base::OneShotTimer web_history_timer_;
-
-  // Observer for HistoryService.
-  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_;
-
-  // The current browser state.
-  ios::ChromeBrowserState* browser_state_;  // weak
-
-  // Delegate for HistoryServiceFacade. Serves as client for HistoryService.
-  __weak id<HistoryServiceFacadeDelegate> delegate_;
-
-  base::WeakPtrFactory<HistoryServiceFacade> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(HistoryServiceFacade);
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_H_
diff --git a/ios/chrome/browser/ui/history/history_service_facade.mm b/ios/chrome/browser/ui/history/history_service_facade.mm
deleted file mode 100644
index d6b391e3..0000000
--- a/ios/chrome/browser/ui/history/history_service_facade.mm
+++ /dev/null
@@ -1,473 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/history/history_service_facade.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/i18n/time_formatting.h"
-#include "base/mac/bind_objc_block.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/browsing_data/core/history_notice_utils.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_types.h"
-#include "components/history/core/browser/web_history_service.h"
-#include "components/keyed_service/core/service_access_type.h"
-#include "components/prefs/pref_service.h"
-#include "components/query_parser/snippet.h"
-#include "components/sync/protocol/history_delete_directive_specifics.pb.h"
-#include "components/sync/protocol/sync_enums.pb.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/history/history_service_factory.h"
-#include "ios/chrome/browser/history/history_utils.h"
-#include "ios/chrome/browser/history/web_history_service_factory.h"
-#include "ios/chrome/browser/pref_names.h"
-#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
-#include "ios/chrome/browser/ui/history/history_entry.h"
-#include "ios/chrome/browser/ui/history/history_service_facade_delegate.h"
-#include "ios/chrome/browser/ui/history/history_util.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// The amount of time to wait for a response from the WebHistoryService.
-static const int kWebHistoryTimeoutSeconds = 3;
-
-namespace {
-
-// Buckets for UMA histograms.
-enum WebHistoryQueryBuckets {
-  WEB_HISTORY_QUERY_FAILED = 0,
-  WEB_HISTORY_QUERY_SUCCEEDED,
-  WEB_HISTORY_QUERY_TIMED_OUT,
-  NUM_WEB_HISTORY_QUERY_BUCKETS
-};
-
-// Returns true if |entry| represents a local visit that had no corresponding
-// visit on the server.
-bool IsLocalOnlyResult(const history::HistoryEntry& entry) {
-  return entry.entry_type == history::HistoryEntry::LOCAL_ENTRY;
-}
-
-// Returns true if there are any differences between the URLs observed deleted
-// and the ones we are expecting to be deleted.
-static bool DeletionsDiffer(const history::URLRows& observed_deletions,
-                            const std::set<GURL>& expected_deletions) {
-  if (observed_deletions.size() != expected_deletions.size())
-    return true;
-  for (const auto& i : observed_deletions) {
-    if (expected_deletions.find(i.url()) == expected_deletions.end())
-      return true;
-  }
-  return false;
-}
-
-}  // namespace
-
-#pragma mark - QueryResult
-
-HistoryServiceFacade::QueryResult::QueryResult()
-    : query(base::string16()),
-      query_start_time(base::string16()),
-      query_end_time(base::string16()),
-      finished(false),
-      sync_returned(false),
-      has_synced_results(false),
-      sync_finished(false),
-      entries(std::vector<history::HistoryEntry>()) {}
-
-HistoryServiceFacade::QueryResult::QueryResult(const QueryResult& other) =
-    default;
-
-HistoryServiceFacade::QueryResult::~QueryResult() {}
-
-#pragma mark - RemovedEntry
-
-HistoryServiceFacade::RemovedEntry::RemovedEntry(const GURL& url,
-                                                 const base::Time& timestamp)
-    : url(url) {
-  timestamps = std::vector<base::Time>();
-  timestamps.push_back(timestamp);
-}
-
-HistoryServiceFacade::RemovedEntry::RemovedEntry(
-    const GURL& url,
-    const std::vector<base::Time>& timestamps)
-    : url(url), timestamps(timestamps) {}
-
-HistoryServiceFacade::RemovedEntry::RemovedEntry(const RemovedEntry& other) =
-    default;
-
-HistoryServiceFacade::RemovedEntry::~RemovedEntry() {}
-
-#pragma mark - HistoryServiceFacade
-
-HistoryServiceFacade::HistoryServiceFacade(
-    ios::ChromeBrowserState* browser_state,
-    id<HistoryServiceFacadeDelegate> delegate)
-    : has_pending_delete_request_(false),
-      history_service_observer_(this),
-      browser_state_(browser_state),
-      delegate_(delegate),
-      weak_factory_(this) {
-  // Register as observer of HistoryService.
-  history::HistoryService* history_service =
-      ios::HistoryServiceFactory::GetForBrowserState(
-          browser_state, ServiceAccessType::EXPLICIT_ACCESS);
-  if (history_service)
-    history_service_observer_.Add(history_service);
-}
-
-HistoryServiceFacade::~HistoryServiceFacade() {
-  query_task_tracker_.TryCancelAll();
-  web_history_request_.reset();
-  delegate_ = nil;
-}
-
-void HistoryServiceFacade::QueryHistory(const base::string16& search_text,
-                                        const history::QueryOptions& options) {
-  // Anything in-flight is invalid.
-  query_task_tracker_.TryCancelAll();
-  web_history_request_.reset();
-
-  // Reset results.
-  query_results_.clear();
-  results_info_value_ = QueryResult();
-
-  // Query synced history.
-  history::WebHistoryService* web_history =
-      ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
-  if (web_history) {
-    web_history_query_results_.clear();
-    web_history_request_ = web_history->QueryHistory(
-        search_text, options,
-        base::Bind(&HistoryServiceFacade::WebHistoryQueryComplete,
-                   base::Unretained(this), search_text, options,
-                   base::TimeTicks::Now()),
-        NO_PARTIAL_TRAFFIC_ANNOTATION_YET);
-    // Start a timer so we know when to give up.
-    web_history_timer_.Start(
-        FROM_HERE, base::TimeDelta::FromSeconds(kWebHistoryTimeoutSeconds),
-        this, &HistoryServiceFacade::WebHistoryTimeout);
-  }
-
-  // Query local history.
-  history::HistoryService* history_service =
-      ios::HistoryServiceFactory::GetForBrowserState(
-          browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (history_service) {
-    history_service->QueryHistory(
-        search_text, options,
-        base::Bind(&HistoryServiceFacade::QueryComplete, base::Unretained(this),
-                   search_text, options),
-        &query_task_tracker_);
-  }
-}
-
-void HistoryServiceFacade::RemoveHistoryEntries(
-    const std::vector<RemovedEntry>& entries) {
-  // Early return if there is a deletion in progress.
-  if (delete_task_tracker_.HasTrackedTasks() || has_pending_delete_request_) {
-    return;
-  }
-
-  history::HistoryService* history_service =
-      ios::HistoryServiceFactory::GetForBrowserState(
-          browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
-  history::WebHistoryService* web_history =
-      ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
-
-  base::Time now = base::Time::Now();
-  std::vector<history::ExpireHistoryArgs> expire_list;
-  expire_list.reserve(entries.size());
-
-  DCHECK(urls_to_be_deleted_.empty());
-  for (const RemovedEntry& entry : entries) {
-    GURL url = entry.url;
-    DCHECK(entry.timestamps.size() > 0);
-
-    // In order to ensure that visits will be deleted from the server and other
-    // clients (even if they are offline), create a sync delete directive for
-    // each visit to be deleted.
-    sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
-    sync_pb::GlobalIdDirective* global_id_directive =
-        delete_directive.mutable_global_id_directive();
-
-    expire_list.resize(expire_list.size() + 1);
-    history::ExpireHistoryArgs* expire_args = &expire_list.back();
-    expire_args->SetTimeRangeForOneDay(entry.timestamps.front());
-    expire_args->urls.insert(entry.url);
-    urls_to_be_deleted_.insert(entry.url);
-
-    for (base::Time visit_time : entry.timestamps) {
-      // The local visit time is treated as a global ID for the visit.
-      global_id_directive->add_global_id(visit_time.ToInternalValue());
-    }
-
-    // Set the start and end time in microseconds since the Unix epoch.
-    global_id_directive->set_start_time_usec(
-        (expire_args->begin_time - base::Time::UnixEpoch()).InMicroseconds());
-
-    // Delete directives shouldn't have an end time in the future.
-    base::Time end_time = std::min(expire_args->end_time, now);
-
-    // -1 because end time in delete directives is inclusive.
-    global_id_directive->set_end_time_usec(
-        (end_time - base::Time::UnixEpoch()).InMicroseconds() - 1);
-
-    if (web_history)
-      history_service->ProcessLocalDeleteDirective(delete_directive);
-  }
-
-  if (history_service) {
-    history_service->ExpireHistory(
-        expire_list, base::Bind(&HistoryServiceFacade::RemoveComplete,
-                                base::Unretained(this)),
-        &delete_task_tracker_);
-  }
-
-  if (web_history) {
-    has_pending_delete_request_ = true;
-    web_history->ExpireHistory(
-        expire_list,
-        base::Bind(&HistoryServiceFacade::RemoveWebHistoryComplete,
-                   weak_factory_.GetWeakPtr()),
-        NO_PARTIAL_TRAFFIC_ANNOTATION_YET);
-  }
-}
-
-void HistoryServiceFacade::QueryOtherFormsOfBrowsingHistory() {
-  browser_sync::ProfileSyncService* sync_service =
-      IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state_);
-  history::WebHistoryService* history_service =
-      ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
-  browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
-      sync_service, history_service,
-      base::Bind(
-          &HistoryServiceFacade::OtherFormsOfBrowsingHistoryQueryComplete,
-          weak_factory_.GetWeakPtr()));
-}
-
-#pragma mark - Private methods
-
-void HistoryServiceFacade::WebHistoryTimeout() {
-  // If there are no outstanding tasks, send results to front end. Would also
-  // be good to communicate the failure to the front end.
-  if (!query_task_tracker_.HasTrackedTasks())
-    ReturnResultsToFrontEnd();
-
-  UMA_HISTOGRAM_ENUMERATION("WebHistory.QueryCompletion",
-                            WEB_HISTORY_QUERY_TIMED_OUT,
-                            NUM_WEB_HISTORY_QUERY_BUCKETS);
-}
-
-void HistoryServiceFacade::QueryComplete(const base::string16& search_text,
-                                         const history::QueryOptions& options,
-                                         history::QueryResults* results) {
-  DCHECK_EQ(0U, query_results_.size());
-  query_results_.reserve(results->size());
-
-  for (const auto& result : *results) {
-    query_results_.push_back(history::HistoryEntry(
-        history::HistoryEntry::LOCAL_ENTRY, result.url(), result.title(),
-        result.visit_time(), std::string(), !search_text.empty(),
-        result.snippet().text(), result.blocked_visit()));
-  }
-
-  results_info_value_.query = search_text;
-  results_info_value_.finished = results->reached_beginning();
-  results_info_value_.query_start_time =
-      history::GetRelativeDateLocalized((options.begin_time));
-
-  // Add the specific dates that were searched to display them.
-  // Should put today if the start is in the future.
-  if (!options.end_time.is_null()) {
-    results_info_value_.query_end_time = history::GetRelativeDateLocalized(
-        options.end_time - base::TimeDelta::FromDays(1));
-  } else {
-    results_info_value_.query_end_time =
-        history::GetRelativeDateLocalized(base::Time::Now());
-  }
-  if (!web_history_timer_.IsRunning())
-    ReturnResultsToFrontEnd();
-}
-
-void HistoryServiceFacade::WebHistoryQueryComplete(
-    const base::string16& search_text,
-    const history::QueryOptions& options,
-    base::TimeTicks start_time,
-    history::WebHistoryService::Request* request,
-    const base::DictionaryValue* results_value) {
-  base::TimeDelta delta = base::TimeTicks::Now() - start_time;
-  UMA_HISTOGRAM_TIMES("WebHistory.ResponseTime", delta);
-
-  // If the response came in too late, do nothing.
-  if (!web_history_timer_.IsRunning())
-    return;
-  web_history_timer_.Stop();
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "WebHistory.QueryCompletion",
-      results_value ? WEB_HISTORY_QUERY_SUCCEEDED : WEB_HISTORY_QUERY_FAILED,
-      NUM_WEB_HISTORY_QUERY_BUCKETS);
-
-  DCHECK_EQ(0U, web_history_query_results_.size());
-  const base::ListValue* events = NULL;
-  if (results_value && results_value->GetList("event", &events)) {
-    web_history_query_results_.reserve(events->GetSize());
-    for (unsigned int i = 0; i < events->GetSize(); ++i) {
-      const base::DictionaryValue* event = NULL;
-      const base::DictionaryValue* result = NULL;
-      const base::ListValue* results = NULL;
-      const base::ListValue* ids = NULL;
-      base::string16 url;
-      base::string16 title;
-      base::Time visit_time;
-
-      if (!(events->GetDictionary(i, &event) &&
-            event->GetList("result", &results) &&
-            results->GetDictionary(0, &result) &&
-            result->GetString("url", &url) && result->GetList("id", &ids) &&
-            ids->GetSize() > 0)) {
-        LOG(WARNING) << "Improperly formed JSON response from history server.";
-        continue;
-      }
-
-      // Ignore any URLs that should not be shown in the history page.
-      GURL gurl(url);
-      if (!ios::CanAddURLToHistory(gurl))
-        continue;
-
-      // Title is optional, so the return value is ignored here.
-      result->GetString("title", &title);
-
-      // Extract the timestamps of all the visits to this URL.
-      // They are referred to as "IDs" by the server.
-      for (int j = 0; j < static_cast<int>(ids->GetSize()); ++j) {
-        const base::DictionaryValue* id = NULL;
-        std::string timestamp_string;
-        int64_t timestamp_usec = 0;
-
-        if (!ids->GetDictionary(j, &id) ||
-            !id->GetString("timestamp_usec", &timestamp_string) ||
-            !base::StringToInt64(timestamp_string, &timestamp_usec)) {
-          NOTREACHED() << "Unable to extract timestamp.";
-          continue;
-        }
-        // The timestamp on the server is a Unix time.
-        base::Time time = base::Time::UnixEpoch() +
-                          base::TimeDelta::FromMicroseconds(timestamp_usec);
-
-        // Get the ID of the client that this visit came from.
-        std::string client_id;
-        id->GetString("client_id", &client_id);
-
-        web_history_query_results_.push_back(history::HistoryEntry(
-            history::HistoryEntry::REMOTE_ENTRY, gurl, title, time, client_id,
-            !search_text.empty(), base::string16(),
-            /* blocked_visit */ false));
-      }
-    }
-  }
-
-  results_info_value_.has_synced_results = results_value != NULL;
-  results_info_value_.sync_returned = true;
-  if (results_value) {
-    std::string continuation_token;
-    results_value->GetString("continuation_token", &continuation_token);
-    results_info_value_.sync_finished = continuation_token.empty();
-  }
-  if (!query_task_tracker_.HasTrackedTasks())
-    ReturnResultsToFrontEnd();
-}
-
-void HistoryServiceFacade::RemoveComplete() {
-  urls_to_be_deleted_.clear();
-
-  // Notify the delegate that the deletion request is complete, but only if a
-  // web history delete request is not still pending.
-  if (has_pending_delete_request_)
-    return;
-  if ([delegate_ respondsToSelector:
-                     @selector(historyServiceFacadeDidCompleteEntryRemoval:)]) {
-    [delegate_ historyServiceFacadeDidCompleteEntryRemoval:this];
-  }
-}
-
-void HistoryServiceFacade::RemoveWebHistoryComplete(bool success) {
-  has_pending_delete_request_ = false;
-  if (!delete_task_tracker_.HasTrackedTasks())
-    RemoveComplete();
-}
-
-void HistoryServiceFacade::OtherFormsOfBrowsingHistoryQueryComplete(
-    bool found_other_forms_of_browsing_history) {
-  if ([delegate_ respondsToSelector:
-                     @selector(historyServiceFacade:
-                         shouldShowNoticeAboutOtherFormsOfBrowsingHistory:)]) {
-    [delegate_ historyServiceFacade:this
-        shouldShowNoticeAboutOtherFormsOfBrowsingHistory:
-            found_other_forms_of_browsing_history];
-  }
-}
-
-void HistoryServiceFacade::ReturnResultsToFrontEnd() {
-  // Combine the local and remote results into |query_results_|, and remove
-  // any duplicates.
-  if (!web_history_query_results_.empty()) {
-    int local_result_count = query_results_.size();
-    query_results_.insert(query_results_.end(),
-                          web_history_query_results_.begin(),
-                          web_history_query_results_.end());
-    history::MergeDuplicateHistoryEntries(&query_results_);
-
-    if (local_result_count) {
-      // In the best case, we expect that all local results are duplicated on
-      // the server. Keep track of how many are missing.
-      int missing_count = std::count_if(
-          query_results_.begin(), query_results_.end(), IsLocalOnlyResult);
-      UMA_HISTOGRAM_PERCENTAGE("WebHistory.LocalResultMissingOnServer",
-                               missing_count * 100.0 / local_result_count);
-    }
-  }
-
-  // Send results to delegate. Results may be empty.
-  results_info_value_.entries = query_results_;
-  if ([delegate_ respondsToSelector:@selector(historyServiceFacade:
-                                             didReceiveQueryResult:)]) {
-    [delegate_ historyServiceFacade:this
-              didReceiveQueryResult:results_info_value_];
-  }
-
-  // Reset results variables.
-  results_info_value_ = QueryResult();
-  query_results_.clear();
-  web_history_query_results_.clear();
-}
-
-void HistoryServiceFacade::OnURLsDeleted(
-    history::HistoryService* history_service,
-    bool all_history,
-    bool expired,
-    const history::URLRows& deleted_rows,
-    const std::set<GURL>& favicon_urls) {
-  if (all_history || DeletionsDiffer(deleted_rows, urls_to_be_deleted_)) {
-    if ([delegate_
-            respondsToSelector:
-                @selector(historyServiceFacadeDidObserveHistoryDeletion:)]) {
-      [delegate_ historyServiceFacadeDidObserveHistoryDeletion:this];
-    }
-  }
-}
diff --git a/ios/chrome/browser/ui/history/history_service_facade_delegate.h b/ios/chrome/browser/ui/history/history_service_facade_delegate.h
deleted file mode 100644
index 5bea82c7..0000000
--- a/ios/chrome/browser/ui/history/history_service_facade_delegate.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_DELEGATE_H_
-
-#include "ios/chrome/browser/ui/history/history_service_facade.h"
-
-// Delegate for HistoryServiceFacade. Defines methods to manage history query
-// results and deletion actions.
-@protocol HistoryServiceFacadeDelegate<NSObject>
-
-@optional
-
-// Notifies the delegate that the result of a history query has been retrieved.
-// Entries in |result| are already sorted.
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-       didReceiveQueryResult:(HistoryServiceFacade::QueryResult)result;
-
-// Notifies the delegate that history entries have been deleted by a different
-// client and that the UI should be updated.
-- (void)historyServiceFacadeDidObserveHistoryDeletion:
-    (HistoryServiceFacade*)facade;
-
-// Indicates to the delegate whether to show notice about other forms of
-// browsing history.
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-    shouldShowNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)shouldShowNotice;
-
-// Notifies the delegate that history entry deletion has completed.
-- (void)historyServiceFacadeDidCompleteEntryRemoval:
-    (HistoryServiceFacade*)facade;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_SERVICE_FACADE_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/history/history_service_facade_unittest.mm b/ios/chrome/browser/ui/history/history_service_facade_unittest.mm
deleted file mode 100644
index ed735d3..0000000
--- a/ios/chrome/browser/ui/history/history_service_facade_unittest.mm
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ui/history/history_service_facade.h"
-
-#include <memory>
-
-#include "base/run_loop.h"
-#include "base/strings/string16.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/keyed_service/core/service_access_type.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/history/history_service_factory.h"
-#import "ios/chrome/browser/ui/history/history_entry.h"
-#import "ios/chrome/browser/ui/history/history_service_facade_delegate.h"
-#include "ios/web/public/test/test_web_thread.h"
-#include "ios/web/public/test/test_web_thread_bundle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#include "third_party/ocmock/gtest_support.h"
-
-namespace {
-
-struct TestResult {
-  GURL url;
-  int64_t hour_offset;  // Visit time in hours past the baseline time.
-};
-
-// Duplicates on the same day in the local timezone are removed, so set a
-// baseline time in local time.
-const base::Time baseline_time = base::Time::UnixEpoch().LocalMidnight();
-
-// Returns true if |result| matches the test data given by |correct_result|,
-// otherwise returns false.
-bool ResultEquals(const history::HistoryEntry& result,
-                  const TestResult& correct_result) {
-  base::Time correct_time =
-      baseline_time + base::TimeDelta::FromHours(correct_result.hour_offset);
-
-  return result.time == correct_time && result.url == correct_result.url;
-}
-
-// Returns RemovedEntry using |time_offset|
-HistoryServiceFacade::RemovedEntry EntryForRemoval(const GURL& url,
-                                                   int64_t time_offset) {
-  return HistoryServiceFacade::RemovedEntry(
-      url, baseline_time + base::TimeDelta::FromHours(time_offset));
-}
-}  // namespace
-
-// Mock delegate for verifying callback behavior.
-@interface MockHistoryServiceFacadeDelegate
-    : NSObject<HistoryServiceFacadeDelegate> {
-  std::vector<history::HistoryEntry> _received_entries;
-}
-@property BOOL didCompleteRemoval;
-@property BOOL didReceiveOtherBrowsingHistoryCallback;
-- (BOOL)delegateDidReceiveResult:(const TestResult&)expected_result;
-@end
-
-@implementation MockHistoryServiceFacadeDelegate
-@synthesize didCompleteRemoval = _didCompleteRemoval;
-@synthesize didReceiveOtherBrowsingHistoryCallback =
-    _didReceiveOtherBrowsingHistoryCallback;
-
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-       didReceiveQueryResult:(HistoryServiceFacade::QueryResult)result {
-  _received_entries = result.entries;
-}
-
-- (void)historyServiceFacade:(HistoryServiceFacade*)facade
-    shouldShowNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)shouldShowNotice {
-  _didReceiveOtherBrowsingHistoryCallback = YES;
-}
-
-// Notifies the delegate that history entry deletion has completed.
-- (void)historyServiceFacadeDidCompleteEntryRemoval:
-    (HistoryServiceFacade*)facade {
-  _didCompleteRemoval = YES;
-}
-
-- (BOOL)delegateDidReceiveResult:(const TestResult&)expected_result {
-  for (history::HistoryEntry entry : _received_entries) {
-    if (ResultEquals(entry, expected_result))
-      return YES;
-  }
-  return NO;
-}
-
-@end
-
-class HistoryServiceFacadeTest : public PlatformTest,
-                                 public history::HistoryServiceObserver {
- public:
-  HistoryServiceFacadeTest() {}
-  ~HistoryServiceFacadeTest() override {}
-
-  void SetUp() override {
-    DCHECK_CURRENTLY_ON(web::WebThread::UI);
-    TestChromeBrowserState::Builder builder;
-    browser_state_ = builder.Build();
-    bool success = browser_state_->CreateHistoryService(true);
-    EXPECT_TRUE(success);
-
-    mock_delegate_ = [[MockHistoryServiceFacadeDelegate alloc] init];
-    history_service_facade_.reset(
-        new HistoryServiceFacade(browser_state_.get(), mock_delegate_));
-  }
-
-  // Cycles the runloop until the condition is met (up to 10 seconds).
-  typedef base::Callback<bool(void)> WaitCondition;
-  bool WaitUntilLoop(WaitCondition condition) {
-    DCHECK_CURRENTLY_ON(web::WebThread::UI);
-    base::Time maxDate = base::Time::Now() + base::TimeDelta::FromSeconds(10);
-    while (!condition.Run()) {
-      if (base::Time::Now() > maxDate)
-        return false;
-      base::RunLoop().RunUntilIdle();
-      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
-    }
-    return true;
-  }
-
-  // Adds a visit to history.
-  void AddVisit(const GURL& url, int64_t time_offset) {
-    history::HistoryService* history_service =
-        ios::HistoryServiceFactory::GetForBrowserStateIfExists(
-            browser_state_.get(), ServiceAccessType::EXPLICIT_ACCESS);
-    EXPECT_TRUE(history_service);
-
-    history_service->AddObserver(this);
-    base::Time time = baseline_time + base::TimeDelta::FromHours(time_offset);
-    history_service->AddPage(url, time, history::VisitSource::SOURCE_BROWSED);
-
-    // Wait until the data is in the db.
-    EXPECT_TRUE(WaitUntilLoop(base::Bind(&HistoryServiceFacadeTest::VerifyVisit,
-                                         base::Unretained(this), url, time)));
-
-    history_service->RemoveObserver(this);
-  }
-
-  // history::HistoryServiceObserver
-  void OnURLVisited(history::HistoryService* history_service,
-                    ui::PageTransition transition,
-                    const history::URLRow& row,
-                    const history::RedirectList& redirects,
-                    base::Time visit_time) override {
-    visited_url_ = row.url();
-    visited_time_ = row.last_visit();
-  }
-
-  // Verify visit to |url| at |time| was observed.
-  bool VerifyVisit(const GURL& url, const base::Time& time) {
-    return visited_url_ == url && visited_time_ == time;
-  }
-
-  // Verify that a query result was received by delegate callback.
-  bool VerifyQueryResult(const TestResult& result) {
-    return [mock_delegate_ delegateDidReceiveResult:result];
-  }
-
-  // Verify that entry removal completion delegate callback was called.
-  bool VerifyEntryRemoval() { return [mock_delegate_ didCompleteRemoval]; }
-
-  // Verify that the
-  // historyServiceFacade:shouldShowNoticeAboutOtherFormsOfBrowsingHistory
-  // delegate callback was called.
-  bool VerifyOtherHistoryCallback() {
-    return [mock_delegate_ didReceiveOtherBrowsingHistoryCallback];
-  }
-
- protected:
-  web::TestWebThreadBundle thread_bundle_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
-  MockHistoryServiceFacadeDelegate* mock_delegate_;
-  std::unique_ptr<HistoryServiceFacade> history_service_facade_;
-  GURL visited_url_;
-  base::Time visited_time_;
-  DISALLOW_COPY_AND_ASSIGN(HistoryServiceFacadeTest);
-};
-
-// Tests that invoking QueryHistory results in the delegate receiving an added
-// history entry.
-TEST_F(HistoryServiceFacadeTest, TestQueryHistory) {
-  GURL url("http://www.testurl.com");
-  AddVisit(url, 0);
-
-  base::string16 query = base::string16();
-  history::QueryOptions options;
-  history_service_facade_->QueryHistory(query, options);
-
-  TestResult expected_result = {url, 0};
-  EXPECT_TRUE(
-      WaitUntilLoop(base::Bind(&HistoryServiceFacadeTest::VerifyQueryResult,
-                               base::Unretained(this), expected_result)));
-}
-
-// Tests that invoking RemoveHistoryEntries completes with callback to delegate
-// method historyServiceFacadeDidCompleteEntryRemoval.
-TEST_F(HistoryServiceFacadeTest, TestRemoveHistoryEntries) {
-  GURL url("http://www.testurl.com");
-  AddVisit(url, 0);
-
-  std::vector<HistoryServiceFacade::RemovedEntry> entries_to_remove{
-      EntryForRemoval(url, 0)};
-  history_service_facade_->RemoveHistoryEntries(entries_to_remove);
-  EXPECT_TRUE(WaitUntilLoop(base::Bind(
-      &HistoryServiceFacadeTest::VerifyEntryRemoval, base::Unretained(this))));
-}
-
-// Tests that invoking QueryOtherFormsOfBrowsingHistory completes with callback
-// to delegate method
-// historyServiceFacade:shouldShowNoticeAboutOtherFormsOfBrowsingHistory:.
-TEST_F(HistoryServiceFacadeTest, TestQueryOtherFormsOfBrowsingHistory) {
-  history_service_facade_->QueryOtherFormsOfBrowsingHistory();
-  EXPECT_TRUE(WaitUntilLoop(
-      base::Bind(&HistoryServiceFacadeTest::VerifyOtherHistoryCallback,
-                 base::Unretained(this))));
-}
diff --git a/ios/chrome/browser/ui/history/history_util.h b/ios/chrome/browser/ui/history/history_util.h
index f6831cf2..04b99ac 100644
--- a/ios/chrome/browser/ui/history/history_util.h
+++ b/ios/chrome/browser/ui/history/history_util.h
@@ -5,8 +5,6 @@
 #ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_UTIL_H_
 #define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_UTIL_H_
 
-#include <vector>
-
 #include "base/strings/string16.h"
 
 namespace base {
@@ -15,16 +13,10 @@
 
 namespace history {
 
-struct HistoryEntry;
-
 // Returns a localized version of |visit_time| including a relative
 // indicator (e.g. today, yesterday).
 base::string16 GetRelativeDateLocalized(const base::Time& visit_time);
 
-// Sorts and merges duplicate entries from the query results, only retaining the
-// most recent visit to a URL on a particular day. That visit contains the
-// timestamps of the other visits.
-void MergeDuplicateHistoryEntries(std::vector<history::HistoryEntry>* entries);
 }  // namespace history
 
 #endif  // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_UTIL_H_
diff --git a/ios/chrome/browser/ui/history/history_util.mm b/ios/chrome/browser/ui/history/history_util.mm
index 6a206ad..f9b6b3a 100644
--- a/ios/chrome/browser/ui/history/history_util.mm
+++ b/ios/chrome/browser/ui/history/history_util.mm
@@ -4,15 +4,11 @@
 
 #include "ios/chrome/browser/ui/history/history_util.h"
 
-#include <map>
-
 #include "base/i18n/time_formatting.h"
 #include "base/time/time.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/ui/history/history_entry.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
-#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -33,44 +29,4 @@
   return date_str;
 }
 
-void MergeDuplicateHistoryEntries(std::vector<history::HistoryEntry>* entries) {
-  std::vector<history::HistoryEntry> new_entries;
-  // Pre-reserve the size of the new vector. Since we're working with pointers
-  // later on not doing this could lead to the vector being resized and to
-  // pointers to invalid locations.
-  new_entries.reserve(entries->size());
-  // Maps a URL to the most recent entry on a particular day.
-  std::map<GURL, history::HistoryEntry*> current_day_entries;
-
-  // Keeps track of the day that |current_day_urls| is holding the URLs for,
-  // in order to handle removing per-day duplicates.
-  base::Time current_day_midnight;
-
-  std::sort(entries->begin(), entries->end(),
-            history::HistoryEntry::SortByTimeDescending);
-
-  for (const history::HistoryEntry& entry : *entries) {
-    // Reset the list of found URLs when a visit from a new day is encountered.
-    if (current_day_midnight != entry.time.LocalMidnight()) {
-      current_day_entries.clear();
-      current_day_midnight = entry.time.LocalMidnight();
-    }
-
-    // Keep this visit if it's the first visit to this URL on the current day.
-    if (current_day_entries.count(entry.url) == 0) {
-      new_entries.push_back(entry);
-      current_day_entries[entry.url] = &new_entries.back();
-    } else {
-      // Keep track of the timestamps of all visits to the URL on the same day.
-      history::HistoryEntry* combined_entry = current_day_entries[entry.url];
-      combined_entry->all_timestamps.insert(entry.all_timestamps.begin(),
-                                            entry.all_timestamps.end());
-
-      if (combined_entry->entry_type != entry.entry_type) {
-        combined_entry->entry_type = history::HistoryEntry::COMBINED_ENTRY;
-      }
-    }
-  }
-  entries->swap(new_entries);
-}
 }  // namespace history
diff --git a/ios/chrome/browser/ui/history/history_util_unittest.mm b/ios/chrome/browser/ui/history/history_util_unittest.mm
deleted file mode 100644
index 5ff62d3..0000000
--- a/ios/chrome/browser/ui/history/history_util_unittest.mm
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ui/history/history_util.h"
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "ios/chrome/browser/ui/history/history_entry.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-struct TestResult {
-  std::string url;
-  int64_t hour_offset;  // Visit time in hours past the baseline time.
-};
-
-// Duplicates on the same day in the local timezone are removed, so set a
-// baseline time in local time.
-const base::Time baseline_time = base::Time::UnixEpoch().LocalMidnight();
-
-// For each item in |results|, create a new Value representing the visit, and
-// insert it into |list_value|.
-void AddQueryResults(TestResult* test_results,
-                     int test_results_size,
-                     std::vector<history::HistoryEntry>* results) {
-  for (int i = 0; i < test_results_size; ++i) {
-    history::HistoryEntry entry;
-    entry.time =
-        baseline_time + base::TimeDelta::FromHours(test_results[i].hour_offset);
-    entry.url = GURL(test_results[i].url);
-    entry.all_timestamps.insert(entry.time.ToInternalValue());
-    results->push_back(entry);
-  }
-}
-
-// Returns true if |result| matches the test data given by |correct_result|,
-// otherwise returns false.
-bool ResultEquals(const history::HistoryEntry& result,
-                  const TestResult& correct_result) {
-  base::Time correct_time =
-      baseline_time + base::TimeDelta::FromHours(correct_result.hour_offset);
-
-  return result.time == correct_time && result.url == GURL(correct_result.url);
-}
-
-}  // namespace
-
-// Tests that the MergeDuplicateResults method correctly removes duplicate
-// visits to the same URL on the same day.
-TEST(HistoryUtilTest, MergeDuplicateResults) {
-  {
-    // Basic test that duplicates on the same day are removed.
-    TestResult test_data[] = {
-        {"http://testurl.com", 0},
-        {"http://testurl.de", 1},
-        {"http://testurl.com", 2},
-        {"http://testurl.com", 3}  // Most recent.
-    };
-    std::vector<history::HistoryEntry> results;
-    AddQueryResults(test_data, arraysize(test_data), &results);
-    history::MergeDuplicateHistoryEntries(&results);
-
-    ASSERT_EQ(2U, results.size());
-    EXPECT_TRUE(ResultEquals(results[0], test_data[3]));
-    EXPECT_TRUE(ResultEquals(results[1], test_data[1]));
-  }
-
-  {
-    // Test that a duplicate URL on the next day is not removed.
-    TestResult test_data[] = {
-        {"http://testurl.com", 0},
-        {"http://testurl.com", 23},
-        {"http://testurl.com", 24},  // Most recent.
-    };
-    std::vector<history::HistoryEntry> results;
-    AddQueryResults(test_data, arraysize(test_data), &results);
-    history::MergeDuplicateHistoryEntries(&results);
-
-    ASSERT_EQ(2U, results.size());
-    EXPECT_TRUE(ResultEquals(results[0], test_data[2]));
-    EXPECT_TRUE(ResultEquals(results[1], test_data[1]));
-  }
-
-  {
-    // Test multiple duplicates across multiple days.
-    TestResult test_data[] = {
-        // First day.
-        {"http://testurl.de", 0},
-        {"http://testurl.com", 1},
-        {"http://testurl.de", 2},
-        {"http://testurl.com", 3},
-
-        // Second day.
-        {"http://testurl.de", 24},
-        {"http://testurl.com", 25},
-        {"http://testurl.de", 26},
-        {"http://testurl.com", 27},  // Most recent.
-    };
-    std::vector<history::HistoryEntry> results;
-    AddQueryResults(test_data, arraysize(test_data), &results);
-    history::MergeDuplicateHistoryEntries(&results);
-
-    ASSERT_EQ(4U, results.size());
-    EXPECT_TRUE(ResultEquals(results[0], test_data[7]));
-    EXPECT_TRUE(ResultEquals(results[1], test_data[6]));
-    EXPECT_TRUE(ResultEquals(results[2], test_data[3]));
-    EXPECT_TRUE(ResultEquals(results[3], test_data[2]));
-  }
-
-  {
-    // Test that timestamps for duplicates are properly saved.
-    TestResult test_data[] = {
-        {"http://testurl.com", 0},
-        {"http://testurl.de", 1},
-        {"http://testurl.com", 2},
-        {"http://testurl.com", 3}  // Most recent.
-    };
-    std::vector<history::HistoryEntry> results;
-    AddQueryResults(test_data, arraysize(test_data), &results);
-    history::MergeDuplicateHistoryEntries(&results);
-
-    ASSERT_EQ(2U, results.size());
-    EXPECT_TRUE(ResultEquals(results[0], test_data[3]));
-    EXPECT_TRUE(ResultEquals(results[1], test_data[1]));
-    EXPECT_EQ(3u, results[0].all_timestamps.size());
-    EXPECT_EQ(1u, results[1].all_timestamps.size());
-  }
-}
diff --git a/ios/chrome/browser/ui/history/ios_browsing_history_driver.h b/ios/chrome/browser/ui/history/ios_browsing_history_driver.h
new file mode 100644
index 0000000..e625c1e4c
--- /dev/null
+++ b/ios/chrome/browser/ui/history/ios_browsing_history_driver.h
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_HISTORY_IOS_BROWSING_HISTORY_DRIVER_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_IOS_BROWSING_HISTORY_DRIVER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/history/core/browser/browsing_history_driver.h"
+#include "components/history/core/browser/browsing_history_service.h"
+#include "url/gurl.h"
+
+namespace history {
+class HistoryService;
+}
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+// Defines methods to manage history query results and deletion actions.
+@protocol BrowsingHistoryDriverDelegate<NSObject>
+
+// Notifies the delegate that the result of a history query has been retrieved.
+// Entries in |result| are already sorted.
+- (void)onQueryCompleteWithResults:
+            (const std::vector<history::BrowsingHistoryService::HistoryEntry>&)
+                results
+                  queryResultsInfo:
+                      (const history::BrowsingHistoryService::QueryResultsInfo&)
+                          queryResultsInfo;
+
+// Notifies the delegate that history entries have been deleted by a different
+// client and that the UI should be updated.
+- (void)didObserverHistoryDeletion;
+
+// Indicates to the delegate whether to show notice about other forms of
+// browsing history.
+- (void)shouldShowNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)shouldShowNotice;
+
+@end
+
+// A simple implementation of BrowsingHistoryServiceHandler that delegates to
+// objective-c object BrowsingHistoryDriverDelegate for most actions.
+class IOSBrowsingHistoryDriver : public history::BrowsingHistoryDriver {
+ public:
+  IOSBrowsingHistoryDriver(ios::ChromeBrowserState* browser_state,
+                           id<BrowsingHistoryDriverDelegate> delegate);
+  ~IOSBrowsingHistoryDriver() override;
+
+ private:
+  // history::BrowsingHistoryDriver implementation.
+  void OnQueryComplete(
+      const std::vector<history::BrowsingHistoryService::HistoryEntry>& results,
+      const history::BrowsingHistoryService::QueryResultsInfo&
+          query_results_info) override;
+  void OnRemoveVisitsComplete() override;
+  void OnRemoveVisitsFailed() override;
+  void OnRemoveVisits(
+      const std::vector<history::ExpireHistoryArgs>& expire_list) override;
+  void HistoryDeleted() override;
+  void HasOtherFormsOfBrowsingHistory(bool has_other_forms,
+                                      bool has_synced_results) override;
+  bool AllowHistoryDeletions() override;
+  bool ShouldHideWebHistoryUrl(const GURL& url) override;
+  history::WebHistoryService* GetWebHistoryService() override;
+  void ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
+      const syncer::SyncService* sync_service,
+      history::WebHistoryService* history_service,
+      base::Callback<void(bool)> callback) override;
+
+  // The current browser state.
+  ios::ChromeBrowserState* browser_state_;  // weak
+
+  // Delegate for IOSBrowsingHistoryDriver. Serves as client for HistoryService.
+  __weak id<BrowsingHistoryDriverDelegate> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOSBrowsingHistoryDriver);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_IOS_BROWSING_HISTORY_DRIVER_H_
diff --git a/ios/chrome/browser/ui/history/ios_browsing_history_driver.mm b/ios/chrome/browser/ui/history/ios_browsing_history_driver.mm
new file mode 100644
index 0000000..01a3770f
--- /dev/null
+++ b/ios/chrome/browser/ui/history/ios_browsing_history_driver.mm
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/history/ios_browsing_history_driver.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/browsing_data/core/history_notice_utils.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/history/history_utils.h"
+#include "ios/chrome/browser/history/web_history_service_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using history::BrowsingHistoryService;
+
+#pragma mark - IOSBrowsingHistoryDriver
+
+IOSBrowsingHistoryDriver::IOSBrowsingHistoryDriver(
+    ios::ChromeBrowserState* browser_state,
+    id<BrowsingHistoryDriverDelegate> delegate)
+    : browser_state_(browser_state), delegate_(delegate) {
+  DCHECK(browser_state_);
+}
+
+IOSBrowsingHistoryDriver::~IOSBrowsingHistoryDriver() {
+  delegate_ = nil;
+}
+
+#pragma mark - Private methods
+
+void IOSBrowsingHistoryDriver::OnQueryComplete(
+    const std::vector<BrowsingHistoryService::HistoryEntry>& results,
+    const BrowsingHistoryService::QueryResultsInfo& query_results_info) {
+  [delegate_ onQueryCompleteWithResults:results
+                       queryResultsInfo:query_results_info];
+}
+
+void IOSBrowsingHistoryDriver::OnRemoveVisitsComplete() {
+  // Ignored.
+}
+
+void IOSBrowsingHistoryDriver::OnRemoveVisitsFailed() {
+  // Ignored.
+}
+
+void IOSBrowsingHistoryDriver::OnRemoveVisits(
+    const std::vector<history::ExpireHistoryArgs>& expire_list) {
+  // Ignored.
+}
+
+void IOSBrowsingHistoryDriver::HistoryDeleted() {
+  [delegate_ didObserverHistoryDeletion];
+}
+
+void IOSBrowsingHistoryDriver::HasOtherFormsOfBrowsingHistory(
+    bool has_other_forms,
+    bool has_synced_results) {
+  [delegate_ shouldShowNoticeAboutOtherFormsOfBrowsingHistory:has_other_forms];
+}
+
+bool IOSBrowsingHistoryDriver::AllowHistoryDeletions() {
+  // Current reasons for suppressing history deletions are from features that
+  // are not currently supported on iOS. Reasons being administrator policy and
+  // supervised users.
+  return true;
+}
+
+bool IOSBrowsingHistoryDriver::ShouldHideWebHistoryUrl(const GURL& url) {
+  return !ios::CanAddURLToHistory(url);
+}
+
+history::WebHistoryService* IOSBrowsingHistoryDriver::GetWebHistoryService() {
+  return ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
+}
+
+void IOSBrowsingHistoryDriver::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
+    const syncer::SyncService* sync_service,
+    history::WebHistoryService* history_service,
+    base::Callback<void(bool)> callback) {
+  browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
+      sync_service, history_service, std::move(callback));
+}
diff --git a/ipc/handle_attachment_fuchsia.cc b/ipc/handle_attachment_fuchsia.cc
index 9d4d13f..88cf518 100644
--- a/ipc/handle_attachment_fuchsia.cc
+++ b/ipc/handle_attachment_fuchsia.cc
@@ -13,13 +13,12 @@
 HandleAttachmentFuchsia::HandleAttachmentFuchsia(const mx_handle_t& handle) {
   mx_status_t result =
       mx_handle_duplicate(handle, MX_RIGHT_SAME_RIGHTS, handle_.receive());
-  DLOG_IF(ERROR, result == MX_OK)
+  DLOG_IF(ERROR, result != MX_OK)
       << "mx_handle_duplicate: " << mx_status_get_string(result);
 }
 
-HandleAttachmentFuchsia::HandleAttachmentFuchsia(const mx_handle_t& handle,
-                                                 FromWire from_wire)
-    : handle_(handle) {}
+HandleAttachmentFuchsia::HandleAttachmentFuchsia(base::ScopedMxHandle handle)
+    : handle_(std::move(handle)) {}
 
 HandleAttachmentFuchsia::~HandleAttachmentFuchsia() {}
 
diff --git a/ipc/handle_attachment_fuchsia.h b/ipc/handle_attachment_fuchsia.h
index 072df0c3..915e4166 100644
--- a/ipc/handle_attachment_fuchsia.h
+++ b/ipc/handle_attachment_fuchsia.h
@@ -22,12 +22,9 @@
   // result. Should only be called by the sender of a Chrome IPC message.
   explicit HandleAttachmentFuchsia(const mx_handle_t& handle);
 
-  enum FromWire {
-    FROM_WIRE,
-  };
   // This constructor takes ownership of |handle|. Should only be called by the
   // receiver of a Chrome IPC message.
-  HandleAttachmentFuchsia(const mx_handle_t& handle, FromWire from_wire);
+  explicit HandleAttachmentFuchsia(base::ScopedMxHandle handle);
 
   Type GetType() const override;
 
diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc
index 006a9ab6..af5b86b 100644
--- a/ipc/ipc_channel_mojo.cc
+++ b/ipc/ipc_channel_mojo.cc
@@ -40,6 +40,10 @@
 #include "ipc/handle_attachment_win.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+#include "ipc/handle_attachment_fuchsia.h"
+#endif
+
 namespace IPC {
 
 namespace {
@@ -92,7 +96,6 @@
 }
 
 #if defined(OS_MACOSX)
-
 MojoResult WrapMachPort(mach_port_t mach_port,
                         mojom::SerializedHandlePtr* serialized) {
   MojoPlatformHandle platform_handle = {
@@ -110,17 +113,31 @@
       mojom::SerializedHandle::Type::MACH_PORT);
   return MOJO_RESULT_OK;
 }
+#elif defined(OS_FUCHSIA)
+MojoResult WrapMxHandle(mx_handle_t handle,
+                        mojom::SerializedHandlePtr* serialized) {
+  MojoPlatformHandle platform_handle = {
+      sizeof(MojoPlatformHandle), MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE,
+      static_cast<uint64_t>(handle)};
 
-#endif
+  MojoHandle wrapped_handle;
+  MojoResult result = MojoWrapPlatformHandle(&platform_handle, &wrapped_handle);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  *serialized = CreateSerializedHandle(
+      mojo::MakeScopedHandle(mojo::Handle(wrapped_handle)),
+      mojom::SerializedHandle::Type::FUCHSIA_HANDLE);
+  return MOJO_RESULT_OK;
+}
+#endif  // defined(OS_FUCHSIA)
 
 #if defined(OS_POSIX)
-
 base::ScopedFD TakeOrDupFile(internal::PlatformFileAttachment* attachment) {
   return attachment->Owns() ? base::ScopedFD(attachment->TakePlatformFile())
                             : base::ScopedFD(dup(attachment->file()));
 }
-
-#endif
+#endif  // defined(OS_POSIX)
 
 MojoResult WrapAttachmentImpl(MessageAttachment* attachment,
                               mojom::SerializedHandlePtr* serialized) {
@@ -146,7 +163,7 @@
                               mojom::SerializedHandle::Type::PLATFORM_FILE,
                               serialized);
   }
-#endif
+#endif  // defined(OS_POSIX)
 #if defined(OS_MACOSX)
   DCHECK_EQ(attachment->GetType(), MessageAttachment::Type::MACH_PORT);
   internal::MachPortAttachmentMac& mach_port_attachment =
@@ -155,6 +172,12 @@
                                    serialized);
   mach_port_attachment.reset_mach_port_ownership();
   return result;
+#elif defined(OS_FUCHSIA)
+  DCHECK_EQ(attachment->GetType(), MessageAttachment::Type::FUCHSIA_HANDLE);
+  internal::HandleAttachmentFuchsia& handle_attachment =
+      static_cast<internal::HandleAttachmentFuchsia&>(*attachment);
+  MojoResult result = WrapMxHandle(handle_attachment.Take(), serialized);
+  return result;
 #elif defined(OS_WIN)
   DCHECK_EQ(attachment->GetType(), MessageAttachment::Type::WIN_HANDLE);
   internal::HandleAttachmentWin& handle_attachment =
@@ -211,8 +234,15 @@
         mach_port, internal::MachPortAttachmentMac::FROM_WIRE);
     return MOJO_RESULT_OK;
   }
-#endif  // defined(OS_MACOSX)
-#if defined(OS_WIN)
+#elif defined(OS_FUCHSIA)
+  if (handle->type == mojom::SerializedHandle::Type::FUCHSIA_HANDLE) {
+    base::ScopedMxHandle handle;
+    if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE)
+      handle.reset(static_cast<mx_handle_t>(platform_handle.value));
+    *attachment = new internal::HandleAttachmentFuchsia(std::move(handle));
+    return MOJO_RESULT_OK;
+  }
+#elif defined(OS_WIN)
   if (handle->type == mojom::SerializedHandle::Type::WIN_HANDLE) {
     base::PlatformFile handle = base::kInvalidPlatformFile;
     if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE)
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 23da3b9..deda35a 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -234,6 +234,10 @@
 const base::Feature kNewRemotePlaybackPipeline{
     "NewRemotePlaybackPipeline", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Set preload to "metadata" by default for <video> and <audio>.
+const base::Feature kPreloadDefaultIsMetadata{
+    "PreloadDefaultIsMetadata", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // CanPlayThrough issued according to standard.
 const base::Feature kSpecCompliantCanPlayThrough{
     "SpecCompliantCanPlayThrough", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 900679b9..37f6c9ef 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -116,6 +116,7 @@
 MEDIA_EXPORT extern const base::Feature kNewAudioRenderingMixingStrategy;
 MEDIA_EXPORT extern const base::Feature kNewRemotePlaybackPipeline;
 MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo;
+MEDIA_EXPORT extern const base::Feature kPreloadDefaultIsMetadata;
 MEDIA_EXPORT extern const base::Feature kResumeBackgroundVideo;
 MEDIA_EXPORT extern const base::Feature kSpecCompliantCanPlayThrough;
 MEDIA_EXPORT extern const base::Feature kSupportExperimentalCdmInterface;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index f1e1e1e..8bdd7ee 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -183,7 +183,9 @@
       network_state_(WebMediaPlayer::kNetworkStateEmpty),
       ready_state_(WebMediaPlayer::kReadyStateHaveNothing),
       highest_ready_state_(WebMediaPlayer::kReadyStateHaveNothing),
-      preload_(MultibufferDataSource::AUTO),
+      preload_(base::FeatureList::IsEnabled(kPreloadDefaultIsMetadata)
+                   ? MultibufferDataSource::METADATA
+                   : MultibufferDataSource::AUTO),
       main_task_runner_(frame->LoadingTaskRunner()),
       media_task_runner_(params->media_task_runner()),
       worker_task_runner_(params->worker_task_runner()),
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc
index efcc248..340153de 100644
--- a/media/formats/mp2t/es_parser_h264.cc
+++ b/media/formats/mp2t/es_parser_h264.cc
@@ -491,10 +491,18 @@
   if (natural_size.width() == 0)
     return false;
 
+  VideoCodecProfile profile =
+      H264Parser::ProfileIDCToVideoCodecProfile(sps->profile_idc);
+  if (profile == VIDEO_CODEC_PROFILE_UNKNOWN) {
+    DVLOG(1) << "Unrecognized SPS profile_idc 0x" << std::hex
+             << sps->profile_idc;
+    return false;
+  }
+
   VideoDecoderConfig video_decoder_config(
-      kCodecH264, H264Parser::ProfileIDCToVideoCodecProfile(sps->profile_idc),
-      PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709, coded_size.value(),
-      visible_rect.value(), natural_size, EmptyExtraData(), scheme);
+      kCodecH264, profile, PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709,
+      coded_size.value(), visible_rect.value(), natural_size, EmptyExtraData(),
+      scheme);
 
   if (!video_decoder_config.Matches(last_video_decoder_config_)) {
     DVLOG(1) << "Profile IDC: " << sps->profile_idc;
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index cb63b77..7e978c2d 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -733,6 +733,7 @@
       video_codec = kCodecH264;
       video_codec_profile = H264Parser::ProfileIDCToVideoCodecProfile(
           avcConfig->profile_indication);
+
       frame_bitstream_converter =
           make_scoped_refptr(new AVCBitstreamConverter(std::move(avcConfig)));
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
@@ -823,6 +824,11 @@
       return false;
   }
 
+  if (video_codec_profile == VIDEO_CODEC_PROFILE_UNKNOWN) {
+    MEDIA_LOG(ERROR, reader->media_log()) << "Unrecognized video codec profile";
+    return false;
+  }
+
   return true;
 }
 
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index 152a4ec..c0fdbc00 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -1502,10 +1502,10 @@
 
   codec_config_->media_crypto = std::move(media_crypto);
   codec_config_->requires_secure_codec = requires_secure_video_codec;
-  // Require a secure surface in all cases, even if we don't require a secure
-  // video codec.  This will send L3 content to a secure surface, if one is
-  // available, as well as L1.
+  // Request a secure surface in all cases.  For L3, it's okay if we fall back
+  // to SurfaceTexture rather than fail composition.  For L1, it's required.
   surface_chooser_state_.is_secure = true;
+  surface_chooser_state_.is_required = requires_secure_video_codec;
 
   // After receiving |media_crypto_| we can start with surface creation.
   StartSurfaceChooser();
diff --git a/media/gpu/android_video_surface_chooser.h b/media/gpu/android_video_surface_chooser.h
index aa0c57c..b444846 100644
--- a/media/gpu/android_video_surface_chooser.h
+++ b/media/gpu/android_video_surface_chooser.h
@@ -20,9 +20,13 @@
  public:
   // Input state used for choosing the surface type.
   struct State {
+    // Is an overlay required?
+    bool is_required = false;
+
+    // Is the player currently in fullscreen?
     bool is_fullscreen = false;
 
-    // Does playback require a secure surface?
+    // Should the overlay be marked as secure?
     bool is_secure = false;
 
     // Is the player's frame hidden / closed?
diff --git a/media/gpu/android_video_surface_chooser_impl.cc b/media/gpu/android_video_surface_chooser_impl.cc
index 06839d1..66ff4d93 100644
--- a/media/gpu/android_video_surface_chooser_impl.cc
+++ b/media/gpu/android_video_surface_chooser_impl.cc
@@ -44,7 +44,8 @@
   // Pre-M, we choose now.  This lets Choose() never worry about the pre-M path.
   if (!allow_dynamic_) {
     if (overlay_factory_ &&
-        (current_state_.is_fullscreen || current_state_.is_secure)) {
+        (current_state_.is_fullscreen || current_state_.is_secure ||
+         current_state_.is_required)) {
       SwitchToOverlay();
     } else {
       SwitchToSurfaceTexture();
@@ -106,6 +107,12 @@
   if (current_state_.is_fullscreen)
     new_overlay_state = kUsingOverlay;
 
+  // Try to use an overlay if possible for protected content.  If the compositor
+  // won't promote, though, it's okay if we switch out.  Set |is_required| in
+  // addition, if you don't want this behavior.
+  if (current_state_.is_secure)
+    new_overlay_state = kUsingOverlay;
+
   // If the compositor won't promote, then don't.
   if (!current_state_.is_compositor_promotable)
     new_overlay_state = kUsingSurfaceTexture;
@@ -118,15 +125,6 @@
       client_overlay_state_ != kUsingOverlay)
     new_overlay_state = kUsingSurfaceTexture;
 
-  // If we need a secure surface, then we must choose an overlay.  The only way
-  // we won't is if we don't have a factory or our request fails.  If the
-  // compositor won't promote, then we still use the overlay, since hopefully
-  // it's a temporary restriction.  If we drop the overlay, then playback will
-  // fail (L1) or be insecure on SurfaceTexture (L3).  For L3, that's still
-  // preferable to failing.
-  if (current_state_.is_secure)
-    new_overlay_state = kUsingOverlay;
-
   // If we're requesting an overlay, check that we haven't asked too recently
   // since the last failure.  This includes L1.  We don't bother to check for
   // our current state, since using an overlay would imply that our most recent
@@ -142,6 +140,11 @@
   if (current_state_.is_frame_hidden)
     new_overlay_state = kUsingSurfaceTexture;
 
+  // If an overlay is required, then choose one.  The only way we won't is if we
+  // don't have a factory or our request fails.
+  if (current_state_.is_required)
+    new_overlay_state = kUsingOverlay;
+
   // If we have no factory, then we definitely don't want to use overlays.
   if (!overlay_factory_)
     new_overlay_state = kUsingSurfaceTexture;
diff --git a/media/gpu/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android_video_surface_chooser_impl_unittest.cc
index 958ba4f..8a63e125 100644
--- a/media/gpu/android_video_surface_chooser_impl_unittest.cc
+++ b/media/gpu/android_video_surface_chooser_impl_unittest.cc
@@ -59,6 +59,7 @@
 enum class ShouldUseOverlay { No, Yes };
 enum class AllowDynamic { No, Yes };
 enum class IsFullscreen { No, Yes };
+enum class IsRequired { No, Yes };
 enum class IsSecure { No, Yes };
 enum class IsFrameHidden { No, Yes };
 enum class IsCCPromotable { No, Yes };
@@ -66,6 +67,7 @@
 
 using TestParams = std::tuple<ShouldUseOverlay,
                               AllowDynamic,
+                              IsRequired,
                               IsFullscreen,
                               IsSecure,
                               IsFrameHidden,
@@ -339,11 +341,12 @@
 
   const bool should_use_overlay = IsYes(ShouldUseOverlay, 0);
   allow_dynamic_ = IsYes(AllowDynamic, 1);
-  chooser_state_.is_fullscreen = IsYes(IsFullscreen, 2);
-  chooser_state_.is_secure = IsYes(IsSecure, 3);
-  chooser_state_.is_frame_hidden = IsYes(IsFrameHidden, 4);
-  chooser_state_.is_compositor_promotable = IsYes(IsCCPromotable, 5);
-  chooser_state_.is_expecting_relayout = IsYes(IsExpectingRelayout, 6);
+  chooser_state_.is_required = IsYes(IsRequired, 2);
+  chooser_state_.is_fullscreen = IsYes(IsFullscreen, 3);
+  chooser_state_.is_secure = IsYes(IsSecure, 4);
+  chooser_state_.is_frame_hidden = IsYes(IsFrameHidden, 5);
+  chooser_state_.is_compositor_promotable = IsYes(IsCCPromotable, 6);
+  chooser_state_.is_expecting_relayout = IsYes(IsExpectingRelayout, 7);
 
   if (should_use_overlay) {
     EXPECT_CALL(client_, UseSurfaceTexture()).Times(0);
@@ -366,62 +369,85 @@
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Either(AllowDynamic),
+                                Values(IsRequired::No),
                                 Values(IsFullscreen::No),
                                 Values(IsSecure::No),
                                 Either(IsFrameHidden),
                                 Either(IsCCPromotable),
                                 Either(IsExpectingRelayout)));
+
 INSTANTIATE_TEST_CASE_P(FullscreenUsesOverlay,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Either(AllowDynamic),
+                                Either(IsRequired),
                                 Values(IsFullscreen::Yes),
                                 Values(IsSecure::No),
                                 Values(IsFrameHidden::No),
                                 Values(IsCCPromotable::Yes),
                                 Values(IsExpectingRelayout::No)));
-INSTANTIATE_TEST_CASE_P(SecureUsesOverlay,
+
+INSTANTIATE_TEST_CASE_P(RequiredUsesOverlay,
+                        AndroidVideoSurfaceChooserImplTest,
+                        Combine(Values(ShouldUseOverlay::Yes),
+                                Values(AllowDynamic::Yes),
+                                Values(IsRequired::Yes),
+                                Either(IsFullscreen),
+                                Either(IsSecure),
+                                Either(IsFrameHidden),
+                                Either(IsCCPromotable),
+                                Either(IsExpectingRelayout)));
+
+// Secure textures should use an overlay if the compositor will promote them.
+// We don't care about relayout, since it's transient; either behavior is okay
+// if a relayout is epected.  Similarly, hidden frames are fine either way.
+INSTANTIATE_TEST_CASE_P(SecureUsesOverlayIfPromotable,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Either(AllowDynamic),
+                                Either(IsRequired),
                                 Either(IsFullscreen),
                                 Values(IsSecure::Yes),
                                 Values(IsFrameHidden::No),
                                 Values(IsCCPromotable::Yes),
-                                Either(IsExpectingRelayout)));
+                                Values(IsExpectingRelayout::No)));
 
 INSTANTIATE_TEST_CASE_P(HiddenFramesUseSurfaceTexture,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(AllowDynamic::Yes),
+                                Values(IsRequired::No),
                                 Either(IsFullscreen),
                                 Either(IsSecure),
                                 Values(IsFrameHidden::Yes),
                                 Either(IsCCPromotable),
                                 Either(IsExpectingRelayout)));
+
 // For all dynamic cases, we shouldn't use an overlay if the compositor won't
-// promote it.  For L1, it will fail either way until the CC supports "must
-// promote" overlays, so we ignore those cases.  Non-dynamic is excluded, since
+// promote it, unless it's marked as required.  This includes secure surfaces,
+// so that L3 will fall back to SurfaceTexture.  Non-dynamic is excluded, since
 // we don't get (or use) compositor feedback before the first frame.  At that
 // point, we've already chosen the output surface and can't switch it.
-INSTANTIATE_TEST_CASE_P(NotCCPromotableNotSecureUsesSurfaceTexture,
+INSTANTIATE_TEST_CASE_P(NotCCPromotableNotRequiredUsesSurfaceTexture,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(AllowDynamic::Yes),
+                                Values(IsRequired::No),
                                 Either(IsFullscreen),
-                                Values(IsSecure::No),
+                                Either(IsSecure),
                                 Values(IsFrameHidden::No),
                                 Values(IsCCPromotable::No),
                                 Either(IsExpectingRelayout)));
 
 // If we're expecting a relayout, then we should never use an overlay unless
-// it's required for a secure output.
+// it's required.
 INSTANTIATE_TEST_CASE_P(InsecureExpectingRelayoutUsesSurfaceTexture,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(AllowDynamic::Yes),
+                                Values(IsRequired::No),
                                 Either(IsFullscreen),
-                                Values(IsSecure::No),
+                                Either(IsSecure),
                                 Either(IsFrameHidden),
                                 Either(IsCCPromotable),
                                 Values(IsExpectingRelayout::Yes)));
@@ -431,6 +457,7 @@
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Values(AllowDynamic::No),
+                                Either(IsRequired),
                                 Values(IsFullscreen::Yes),
                                 Either(IsSecure),
                                 Either(IsFrameHidden),
@@ -442,10 +469,23 @@
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Values(AllowDynamic::No),
+                                Either(IsRequired),
                                 Either(IsFullscreen),
                                 Values(IsSecure::Yes),
                                 Either(IsFrameHidden),
                                 Either(IsCCPromotable),
                                 Either(IsExpectingRelayout)));
 
+// "is_required" should be enough to trigger an overlay pre-M.
+INSTANTIATE_TEST_CASE_P(NotDynamicRequiredUsesOverlay,
+                        AndroidVideoSurfaceChooserImplTest,
+                        Combine(Values(ShouldUseOverlay::Yes),
+                                Values(AllowDynamic::No),
+                                Values(IsRequired::Yes),
+                                Either(IsFullscreen),
+                                Either(IsSecure),
+                                Either(IsFrameHidden),
+                                Either(IsCCPromotable),
+                                Either(IsExpectingRelayout)));
+
 }  // namespace media
diff --git a/media/video/h264_parser.cc b/media/video/h264_parser.cc
index 5e1f657..d32a85a 100644
--- a/media/video/h264_parser.cc
+++ b/media/video/h264_parser.cc
@@ -426,7 +426,7 @@
     case H264SPS::kProfileIDSMultiviewHigh:
       return H264PROFILE_MULTIVIEWHIGH;
   }
-  NOTREACHED() << "unknown video profile: " << profile_idc;
+  DVLOG(1) << "unknown video profile: " << profile_idc;
   return VIDEO_CODEC_PROFILE_UNKNOWN;
 }
 
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
index 2624a65..aa86621 100644
--- a/mojo/edk/embedder/platform_handle.h
+++ b/mojo/edk/embedder/platform_handle.h
@@ -16,8 +16,11 @@
 #include <mach/mach.h>
 #elif defined(OS_FUCHSIA)
 #include <magenta/syscalls.h>
+#include <mxio/limits.h>
 #endif
 
+#include "base/logging.h"
+
 namespace mojo {
 namespace edk {
 
@@ -34,6 +37,7 @@
   }
   static PlatformHandle ForFd(int fd) {
     PlatformHandle platform_handle;
+    DCHECK_LT(fd, MAX_MXIO_FD);
     platform_handle.fd = fd;
     return platform_handle;
   }
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
index 0b2657a4..e27d13f 100644
--- a/mojo/public/cpp/bindings/README.md
+++ b/mojo/public/cpp/bindings/README.md
@@ -620,7 +620,7 @@
 ```cpp
 union Value {
   int64 int_value;
-  float32 float_vlaue;
+  float32 float_value;
   string string_value;
 };
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 7e6622a..fbb0b0da 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2657,7 +2657,7 @@
     public_deps += [ "//crypto:platform" ]
   }
 
-  if (is_android) {
+  if (is_android || is_fuchsia) {
     sources += [
       "test/spawned_test_server/remote_test_server.cc",
       "test/spawned_test_server/remote_test_server.h",
@@ -5358,6 +5358,10 @@
     ]
   }
 
+  if (is_fuchsia) {
+    use_test_server = true
+  }
+
   if (!use_nss_certs) {
     sources -= [
       "cert/internal/trust_store_nss_unittest.cc",
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index 1eb2d28..568fc05 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -258,8 +258,6 @@
   UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfJobController",
                           job_controller_set_.size());
 
-  int alt_job_count = 0;
-  int main_job_count = 0;
   size_t num_controllers_with_request = 0;
   size_t num_controllers_for_preconnect = 0;
   for (const auto& job_controller : job_controller_set_) {
@@ -277,10 +275,6 @@
     // For non-preconnects.
     if (job_controller->HasPendingRequest())
       num_controllers_with_request++;
-    if (job_controller->HasPendingAltJob())
-      alt_job_count++;
-    if (job_controller->HasPendingMainJob())
-      main_job_count++;
   }
   UMA_HISTOGRAM_COUNTS_1M(
       "Net.JobControllerSet.CountOfJobController.Preconnect",
@@ -288,16 +282,10 @@
   UMA_HISTOGRAM_COUNTS_1M(
       "Net.JobControllerSet.CountOfJobController.NonPreconnect.PendingRequest",
       num_controllers_with_request);
-
   UMA_HISTOGRAM_COUNTS_1M(
       "Net.JobControllerSet.CountOfJobController.NonPreconnect.RequestGone",
       job_controller_set_.size() - num_controllers_for_preconnect -
           num_controllers_with_request);
-
-  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectAltJob",
-                          alt_job_count);
-  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectMainJob",
-                          main_job_count);
 }
 
 void HttpStreamFactoryImpl::DumpMemoryStats(
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 2f73ecc..f8c3d5b 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -34884,6 +34884,7 @@
       "expect_staple": true, "include_subdomains_for_expect_staple": true,
       "expect_staple_report_uri": "https://photistic.report-uri.io/r/default/staple/reportOnly"
     },
+    { "name": "ccu.plus", "include_subdomains": true, "mode": "force-https" },
     // END OF MANUAL ENTRIES
 
     // TODO(lgarron): hstspreload.org can't scan IPv6-only sites due to Google
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index f7292b5..0caa4719 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -1211,6 +1211,9 @@
     base::ResetAndReturn(&callback_).Run(OK);
   }
   if (event == HANDSHAKE_CONFIRMED) {
+    if (stream_factory_)
+      stream_factory_->set_require_confirmation(false);
+
     // Update |connect_end| only when handshake is confirmed. This should also
     // take care of any failed 0-RTT request.
     connect_timing_.connect_end = base::TimeTicks::Now();
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index b0bc10e9..b5ac9c0c8 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -1487,6 +1487,10 @@
     if (http_server_properties_->GetSupportsQuic(&last_address) &&
         last_address == local_address_.address()) {
       require_confirmation_ = false;
+      // Clear the persisted IP address, in case the network no longer supports
+      // QUIC so the next restart will require confirmation. It will be
+      // re-persisted when the first job completes successfully.
+      http_server_properties_->SetSupportsQuic(false, last_address);
     }
   }
 
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index aed21a2a..06b4389 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -892,9 +892,14 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             &net_error_details_, callback_.callback()));
 
+  IPAddress last_address;
+  EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address));
+
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
       QuicSession::HANDSHAKE_CONFIRMED);
 
+  EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address));
+
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
   std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
@@ -927,11 +932,19 @@
                               &net_error_details_, callback_.callback()),
               IsOk());
 
+  IPAddress last_address;
+  EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address));
+
   std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
   EXPECT_FALSE(session->require_confirmation());
+
+  crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+      QuicSession::HANDSHAKE_CONFIRMED);
+
+  EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address));
 }
 
 TEST_P(QuicStreamFactoryTest, CachedInitialRtt) {
@@ -1782,11 +1795,14 @@
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                          net_log_, CompletionCallback()));
 
+  IPAddress last_address;
+  EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address));
   // Change the IP address and verify that stream saw the error.
   NotifyIPAddressChanged();
   EXPECT_EQ(ERR_NETWORK_CHANGED,
             stream->ReadResponseHeaders(callback_.callback()));
   EXPECT_TRUE(factory_->require_confirmation());
+  EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address));
 
   // Now attempting to request a stream to the same origin should create
   // a new session.
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index d80326d..a2b3d32 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -2388,10 +2388,14 @@
     return nullptr;
   }
 
+  SpdyStream* stream = active_it->second;
   net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_ADOPTED_PUSH_STREAM,
                     base::Bind(&NetLogSpdyAdoptedPushStreamCallback,
-                               active_it->second->stream_id(), &url));
-  return active_it->second;
+                               stream->stream_id(), &url));
+  // A stream is in reserved remote state until response headers arrive.
+  UMA_HISTOGRAM_BOOLEAN("Net.PushedStreamAlreadyHasResponseHeaders",
+                        !stream->IsReservedRemote());
+  return stream;
 }
 
 void SpdySession::RecordPingRTTHistogram(base::TimeDelta duration) {
diff --git a/net/test/spawned_test_server/spawned_test_server.h b/net/test/spawned_test_server/spawned_test_server.h
index aa4e37a..47232ba 100644
--- a/net/test/spawned_test_server/spawned_test_server.h
+++ b/net/test/spawned_test_server/spawned_test_server.h
@@ -7,7 +7,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
 #include "net/test/spawned_test_server/remote_test_server.h"
 #else
 #include "net/test/spawned_test_server/local_test_server.h"
@@ -15,7 +15,7 @@
 
 namespace net {
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
 typedef RemoteTestServer SpawnedTestServer;
 #else
 typedef LocalTestServer SpawnedTestServer;
diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc
index 66d6590..22737da 100644
--- a/printing/backend/print_backend_cups.cc
+++ b/printing/backend/print_backend_cups.cc
@@ -11,7 +11,6 @@
 
 #include <string>
 
-#include "base/debug/leak_annotations.h"
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -217,15 +216,8 @@
 #endif  // !defined(OS_CHROMEOS)
 
 int PrintBackendCUPS::GetDests(cups_dest_t** dests) {
-  if (print_server_url_.is_empty()) {  // Use default (local) print server.
-    // GnuTLS has a genuine small memory leak that is easier to annotate
-    // than suppress. See http://crbug.com/176888#c7
-    // In theory any CUPS function can trigger this leak, but in
-    // PrintBackendCUPS, this is the most likely spot.
-    // TODO(eugenis): remove this once the leak is fixed.
-    ANNOTATE_SCOPED_MEMORY_LEAK;
+  if (print_server_url_.is_empty())  // Use default (local) print server.
     return cupsGetDests(dests);
-  }
 
   HttpConnectionCUPS http(print_server_url_, cups_encryption_);
   http.SetBlocking(blocking_);
diff --git a/remoting/client/gesture_interpreter.cc b/remoting/client/gesture_interpreter.cc
index bb20d21..1dcec9f 100644
--- a/remoting/client/gesture_interpreter.cc
+++ b/remoting/client/gesture_interpreter.cc
@@ -13,7 +13,7 @@
 
 namespace {
 
-const float kOneFingerFlingTimeConstant = 325.f;
+const float kOneFingerFlingTimeConstant = 180.f;
 const float kScrollFlingTimeConstant = 250.f;
 
 }  // namespace
diff --git a/remoting/client/ui/fling_tracker.cc b/remoting/client/ui/fling_tracker.cc
index 28c59fd5..29fc8363 100644
--- a/remoting/client/ui/fling_tracker.cc
+++ b/remoting/client/ui/fling_tracker.cc
@@ -14,15 +14,13 @@
 
 // TODO(yuweih): May need to tweak these numbers to get better smoothness.
 
-// Stop flinging if the speed drops below this.
-// 0.5px per 16ms. i.e. 0.5px/frame.
-// TODO(yuweih): The screen unit may not be in pixel. This needs to be
-//               normalized with the DPI.
-const float kMinTrackSpeed = 0.03125f;
+// Stop flinging if the speed drops below this. This is a small speed to make
+// sure the animation stops smoothly.
+const float kMinTrackSpeed = 0.01f;
 
 // The minimum displacement needed to trigger the fling animation. This is to
 // prevent unintentional fling with low velocity.
-const float kMinDisplacement = 50.f;
+const float kMinDisplacement = 20.f;
 
 float GetDisplacement(float time_constant,
                       float initial_speed_rate,
diff --git a/services/catalog/entry.cc b/services/catalog/entry.cc
index de4a3eb..3b6427a2 100644
--- a/services/catalog/entry.cc
+++ b/services/catalog/entry.cc
@@ -158,6 +158,11 @@
   }
   entry->set_display_name(std::move(display_name));
 
+  // Sandbox type, optional.
+  std::string sandbox_type;
+  if (value.GetString(Store::kSandboxTypeKey, &sandbox_type))
+    entry->set_sandbox_type(std::move(sandbox_type));
+
   // InterfaceProvider specs.
   const base::DictionaryValue* interface_provider_specs = nullptr;
   if (!value.GetDictionary(Store::kInterfaceProviderSpecsKey,
@@ -221,8 +226,8 @@
 }
 
 bool Entry::operator==(const Entry& other) const {
-  return other.name_ == name_ &&
-         other.display_name_ == display_name_ &&
+  return other.name_ == name_ && other.display_name_ == display_name_ &&
+         other.sandbox_type_ == sandbox_type_ &&
          other.interface_provider_specs_ == interface_provider_specs_;
 }
 
diff --git a/services/catalog/entry.h b/services/catalog/entry.h
index 8a62ab9..2320f25c 100644
--- a/services/catalog/entry.h
+++ b/services/catalog/entry.h
@@ -44,6 +44,11 @@
     display_name_ = std::move(display_name);
   }
 
+  const std::string& sandbox_type() const { return sandbox_type_; }
+  void set_sandbox_type(std::string sandbox_type) {
+    sandbox_type_ = std::move(sandbox_type);
+  }
+
   const Entry* parent() const { return parent_; }
   void set_parent(const Entry* parent) { parent_ = parent; }
 
@@ -71,6 +76,7 @@
   std::string name_;
   base::FilePath path_;
   std::string display_name_;
+  std::string sandbox_type_;
   service_manager::InterfaceProviderSpecMap interface_provider_specs_;
   std::map<std::string, base::FilePath> required_file_paths_;
   const Entry* parent_ = nullptr;
diff --git a/services/catalog/entry_unittest.cc b/services/catalog/entry_unittest.cc
index e5dba1b..3406c4e 100644
--- a/services/catalog/entry_unittest.cc
+++ b/services/catalog/entry_unittest.cc
@@ -58,12 +58,14 @@
   std::unique_ptr<Entry> entry = ReadEntry("simple", nullptr);
   EXPECT_EQ("foo", entry->name());
   EXPECT_EQ("Foo", entry->display_name());
+  EXPECT_EQ("none", entry->sandbox_type());
 }
 
 TEST_F(EntryTest, Instance) {
   std::unique_ptr<Entry> entry = ReadEntry("instance", nullptr);
   EXPECT_EQ("foo", entry->name());
   EXPECT_EQ("Foo", entry->display_name());
+  EXPECT_EQ("", entry->sandbox_type());
 }
 
 TEST_F(EntryTest, ConnectionSpec) {
diff --git a/services/catalog/store.cc b/services/catalog/store.cc
index 70d73ac1..f59e6d8 100644
--- a/services/catalog/store.cc
+++ b/services/catalog/store.cc
@@ -11,6 +11,8 @@
 // static
 const char Store::kDisplayNameKey[] = "display_name";
 // static
+const char Store::kSandboxTypeKey[] = "sandbox_type";
+// static
 const char Store::kInterfaceProviderSpecsKey[] = "interface_provider_specs";
 // static
 const char Store::kInterfaceProviderSpecs_ProvidesKey[] = "provides";
diff --git a/services/catalog/store.h b/services/catalog/store.h
index 6bef188..831ee9f 100644
--- a/services/catalog/store.h
+++ b/services/catalog/store.h
@@ -15,6 +15,8 @@
   static const char kNameKey[];
   // Value is a string.
   static const char kDisplayNameKey[];
+  // Value is a string.
+  static const char kSandboxTypeKey[];
   // Value is a dictionary.
   static const char kInterfaceProviderSpecsKey[];
   // Value is a dictionary.
diff --git a/services/catalog/test_data/simple b/services/catalog/test_data/simple
index 3fa31be..04cd6fe 100644
--- a/services/catalog/test_data/simple
+++ b/services/catalog/test_data/simple
@@ -1,5 +1,6 @@
 {
   "name": "foo",
   "display_name": "Foo",
+  "sandbox_type": "none",
   "interface_provider_specs": { }
 }
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 65ae037..d7a4b74 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -204,6 +204,10 @@
 #   define SK_USE_LEGACY_DISTANCE_FIELDS
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT
+#define SK_SUPPORT_LEGACY_LINEAR_GRADIENT
+#endif
+
 #ifndef SK_SUPPORT_LEGACY_STREAM_API
 #define SK_SUPPORT_LEGACY_STREAM_API
 #endif
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index b1c86a29..60433e5 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1036,7 +1036,7 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "4",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -1051,7 +1051,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 6
         },
         "test": "content_browsertests"
       },
@@ -6859,7 +6860,7 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "4",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6874,7 +6875,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 6
         },
         "test": "content_browsertests"
       },
@@ -6903,7 +6905,7 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "4",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6918,7 +6920,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 6
         },
         "test": "content_browsertests"
       },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index a7acd2143..b708ca22 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -746,6 +746,16 @@
         "test": "base_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/browser_tests_cros_asan.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1000,6 +1010,15 @@
         "test": "ui_touch_selection_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/unit_tests_cros_asan.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/filters/browser_tests_cros_asan.filter b/testing/buildbot/filters/browser_tests_cros_asan.filter
new file mode 100644
index 0000000..94c0b93
--- /dev/null
+++ b/testing/buildbot/filters/browser_tests_cros_asan.filter
@@ -0,0 +1,2 @@
+# TODO(crbug.com/759291): Test is flakey with ASan.
+-SamlTest.ScrapedMultiple
diff --git a/testing/buildbot/filters/fuchsia.content_unittests.filter b/testing/buildbot/filters/fuchsia.content_unittests.filter
index 064debb..19d3b20 100644
--- a/testing/buildbot/filters/fuchsia.content_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.content_unittests.filter
@@ -1,6 +1,5 @@
 # Being ported, https://crbug.com/754861.
 
--AudioRendererMixerManagerTest.MixerParamsLatencyRtc
 -AudioRendererSinkCacheTest.SmokeTest
 -BaseFileTest.ReadonlyBaseFile
 -BaseFileTest.RenameWithError
diff --git a/testing/buildbot/filters/fuchsia.net_unittests.filter b/testing/buildbot/filters/fuchsia.net_unittests.filter
index 619c9dc..cf21bd1d 100644
--- a/testing/buildbot/filters/fuchsia.net_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.net_unittests.filter
@@ -15,6 +15,7 @@
 -EmbeddedTestServer*
 -HostResolverImplDnsTest.DnsTask
 -HttpCache.RangeGET_ParallelValidationDifferentRanges  # https://crbug.com/755552
+-HttpCache.RangeGET_ParallelValidationOverlappingRanges  # https://crbug.com/758221
 -HttpNetworkTransactionTest.UploadUnreadableFile
 -HttpServerTest.*
 -NetworkInterfacesTest.GetNetworkList
diff --git a/testing/buildbot/filters/mus.browser_tests.filter b/testing/buildbot/filters/mus.browser_tests.filter
index 1137157..853337c4 100644
--- a/testing/buildbot/filters/mus.browser_tests.filter
+++ b/testing/buildbot/filters/mus.browser_tests.filter
@@ -56,3 +56,6 @@
 -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/0
 -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/1
 -WebViewTests/WebViewTest.InterstitialPageFocusedWidget/1
+
+# TODO: fix, http://crbug.com/759721
+-VirtualKeyboardBrowserTest.HideKeyboardKeyTest
diff --git a/testing/buildbot/filters/unit_tests_cros_asan.filter b/testing/buildbot/filters/unit_tests_cros_asan.filter
new file mode 100644
index 0000000..a0bb500
--- /dev/null
+++ b/testing/buildbot/filters/unit_tests_cros_asan.filter
@@ -0,0 +1,2 @@
+# TODO(crbug.com/756844): Fix memory leaks.
+-SafeBrowsingBlockingQuietPageTests.*
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html b/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
index 1a67d6e..f958b2e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
@@ -120,7 +120,7 @@
   assert_false(media.hasAttribute('preload'));
 
   // Test allowed values.
-  [ '', 'none', 'metadata', 'auto' ].forEach(t.step_func(preload => {
+  [ 'none', 'metadata', 'auto' ].forEach(t.step_func(preload => {
     var expected = test.expectations.allowed.includes(preload) ? preload : test.expectations.default;
     checkPreloadAttribute(media, preload, expected);
   }));
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn
index ece2de7..f14c3796 100644
--- a/third_party/WebKit/Source/core/editing/BUILD.gn
+++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -220,12 +220,12 @@
     "markers/DocumentMarkerController.h",
     "markers/DocumentMarkerList.cpp",
     "markers/DocumentMarkerList.h",
-    "markers/DocumentMarkerListEditor.cpp",
-    "markers/DocumentMarkerListEditor.h",
     "markers/GrammarMarker.cpp",
     "markers/GrammarMarker.h",
     "markers/GrammarMarkerListImpl.cpp",
     "markers/GrammarMarkerListImpl.h",
+    "markers/SortedDocumentMarkerListEditor.cpp",
+    "markers/SortedDocumentMarkerListEditor.h",
     "markers/SpellCheckMarker.cpp",
     "markers/SpellCheckMarker.h",
     "markers/SpellCheckMarkerListImpl.cpp",
@@ -358,10 +358,10 @@
     "markers/CompositionMarkerListImplTest.cpp",
     "markers/CompositionMarkerTest.cpp",
     "markers/DocumentMarkerControllerTest.cpp",
-    "markers/DocumentMarkerListEditorTest.cpp",
     "markers/DocumentMarkerTest.cpp",
     "markers/GrammarMarkerListImplTest.cpp",
     "markers/GrammarMarkerTest.cpp",
+    "markers/SortedDocumentMarkerListEditorTest.cpp",
     "markers/SpellingMarkerListImplTest.cpp",
     "markers/SpellingMarkerTest.cpp",
     "markers/SuggestionMarkerListImplTest.cpp",
diff --git a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
index be35da3..a053005 100644
--- a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
@@ -4,7 +4,7 @@
 
 #include "core/editing/markers/ActiveSuggestionMarkerListImpl.h"
 
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 
 namespace blink {
 
@@ -17,8 +17,8 @@
 }
 
 void ActiveSuggestionMarkerListImpl::Add(DocumentMarker* marker) {
-  DocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
-                                                               marker);
+  SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
+                                                                     marker);
 }
 
 void ActiveSuggestionMarkerListImpl::Clear() {
@@ -33,7 +33,7 @@
 DocumentMarker* ActiveSuggestionMarkerListImpl::FirstMarkerIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return DocumentMarkerListEditor::FirstMarkerIntersectingRange(
+  return SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
@@ -41,27 +41,28 @@
 ActiveSuggestionMarkerListImpl::MarkersIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return DocumentMarkerListEditor::MarkersIntersectingRange(
+  return SortedDocumentMarkerListEditor::MarkersIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 bool ActiveSuggestionMarkerListImpl::MoveMarkers(
     int length,
     DocumentMarkerList* dst_markers_) {
-  return DocumentMarkerListEditor::MoveMarkers(&markers_, length, dst_markers_);
+  return SortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
+                                                     dst_markers_);
 }
 
 bool ActiveSuggestionMarkerListImpl::RemoveMarkers(unsigned start_offset,
                                                    int length) {
-  return DocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
-                                                 length);
+  return SortedDocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
+                                                       length);
 }
 
 bool ActiveSuggestionMarkerListImpl::ShiftMarkers(const String&,
                                                   unsigned offset,
                                                   unsigned old_length,
                                                   unsigned new_length) {
-  return DocumentMarkerListEditor::ShiftMarkersContentIndependent(
+  return SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
       &markers_, offset, old_length, new_length);
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
index b709959..abcee75 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
@@ -4,7 +4,7 @@
 
 #include "core/editing/markers/CompositionMarkerListImpl.h"
 
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 
 namespace blink {
 
@@ -17,8 +17,8 @@
 }
 
 void CompositionMarkerListImpl::Add(DocumentMarker* marker) {
-  DocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
-                                                               marker);
+  SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
+                                                                     marker);
 }
 
 void CompositionMarkerListImpl::Clear() {
@@ -33,33 +33,34 @@
 DocumentMarker* CompositionMarkerListImpl::FirstMarkerIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return DocumentMarkerListEditor::FirstMarkerIntersectingRange(
+  return SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 HeapVector<Member<DocumentMarker>>
 CompositionMarkerListImpl::MarkersIntersectingRange(unsigned start_offset,
                                                     unsigned end_offset) const {
-  return DocumentMarkerListEditor::MarkersIntersectingRange(
+  return SortedDocumentMarkerListEditor::MarkersIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 bool CompositionMarkerListImpl::MoveMarkers(int length,
                                             DocumentMarkerList* dst_markers_) {
-  return DocumentMarkerListEditor::MoveMarkers(&markers_, length, dst_markers_);
+  return SortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
+                                                     dst_markers_);
 }
 
 bool CompositionMarkerListImpl::RemoveMarkers(unsigned start_offset,
                                               int length) {
-  return DocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
-                                                 length);
+  return SortedDocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
+                                                       length);
 }
 
 bool CompositionMarkerListImpl::ShiftMarkers(const String&,
                                              unsigned offset,
                                              unsigned old_length,
                                              unsigned new_length) {
-  return DocumentMarkerListEditor::ShiftMarkersContentIndependent(
+  return SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
       &markers_, offset, old_length, new_length);
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
index d940bbb..83ac6f1 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -38,9 +38,9 @@
 #include "core/editing/markers/ActiveSuggestionMarkerListImpl.h"
 #include "core/editing/markers/CompositionMarker.h"
 #include "core/editing/markers/CompositionMarkerListImpl.h"
-#include "core/editing/markers/DocumentMarkerListEditor.h"
 #include "core/editing/markers/GrammarMarker.h"
 #include "core/editing/markers/GrammarMarkerListImpl.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 #include "core/editing/markers/SpellingMarker.h"
 #include "core/editing/markers/SpellingMarkerListImpl.h"
 #include "core/editing/markers/SuggestionMarker.h"
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditorTest.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditorTest.cpp
deleted file mode 100644
index 0dcb40b..0000000
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditorTest.cpp
+++ /dev/null
@@ -1,561 +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 "core/editing/markers/DocumentMarkerListEditor.h"
-
-#include "core/editing/markers/TextMatchMarker.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-class DocumentMarkerListEditorTest : public ::testing::Test {
- protected:
-  DocumentMarker* CreateMarker(unsigned startOffset, unsigned endOffset) {
-    return new TextMatchMarker(startOffset, endOffset,
-                               TextMatchMarker::MatchStatus::kInactive);
-  }
-};
-
-TEST_F(DocumentMarkerListEditorTest, RemoveMarkersEmptyList) {
-  DocumentMarkerListEditor::MarkerList markers;
-  DocumentMarkerListEditor::RemoveMarkers(&markers, 0, 10);
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest, RemoveMarkersTouchingEndpoints) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-  markers.push_back(CreateMarker(10, 20));
-  markers.push_back(CreateMarker(20, 30));
-
-  DocumentMarkerListEditor::RemoveMarkers(&markers, 10, 10);
-
-  EXPECT_EQ(2u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-
-  EXPECT_EQ(20u, markers[1]->StartOffset());
-  EXPECT_EQ(30u, markers[1]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, RemoveMarkersOneCharacterIntoInterior) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-  markers.push_back(CreateMarker(10, 20));
-  markers.push_back(CreateMarker(20, 30));
-
-  DocumentMarkerListEditor::RemoveMarkers(&markers, 9, 12);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceStartOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 5, 5);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceStartOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  // Replace with shorter text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 5, 4);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(9u, markers[0]->EndOffset());
-
-  // Replace with longer text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 4, 5);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-
-  // Replace with text of same length
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 5, 5);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceContainsStartOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 15));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10, 10);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceContainsStartOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 15));
-
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 10, 10);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(10u, markers[0]->StartOffset());
-  EXPECT_EQ(15u, markers[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceEndOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 5, 5);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceEndOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  // Replace with shorter text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 5, 4);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(9u, markers[0]->EndOffset());
-
-  // Replace with longer text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 4, 5);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-
-  // Replace with text of same length
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 5, 5);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceContainsEndOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 10, 10);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceContainsEndOfMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 10, 10);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceEntireMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10, 10);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceEntireMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  // Replace with shorter text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 10, 9);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(9u, markers[0]->EndOffset());
-
-  // Replace with longer text
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 9, 10);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-
-  // Replace with text of same length
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 10, 10);
-
-  EXPECT_EQ(1u, markers.size());
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(10u, markers[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceTextWithMarkerAtBeginning) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 15, 15);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceTextWithMarkerAtBeginning) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 15, 15);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_ReplaceTextWithMarkerAtEnd) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 15));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 15, 15);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_ReplaceTextWithMarkerAtEnd) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 15));
-
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 15, 15);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest, ContentDependentMarker_Deletions) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-  markers.push_back(CreateMarker(15, 20));
-  markers.push_back(CreateMarker(20, 25));
-
-  // Delete range containing the end of the second marker, the entire third
-  // marker, and the start of the fourth marker
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 8, 9, 0);
-
-  EXPECT_EQ(2u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(11u, markers[1]->StartOffset());
-  EXPECT_EQ(16u, markers[1]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, ContentIndependentMarker_Deletions) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-  markers.push_back(CreateMarker(15, 20));
-  markers.push_back(CreateMarker(20, 25));
-
-  // Delete range containing the end of the second marker, the entire third
-  // marker, and the start of the fourth marker
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 8, 9, 0);
-
-  EXPECT_EQ(4u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(5u, markers[1]->StartOffset());
-  EXPECT_EQ(8u, markers[1]->EndOffset());
-
-  EXPECT_EQ(8u, markers[2]->StartOffset());
-  EXPECT_EQ(11u, markers[2]->EndOffset());
-
-  EXPECT_EQ(11u, markers[3]->StartOffset());
-  EXPECT_EQ(16u, markers[3]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_DeleteExactlyOnMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10, 0);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_DeleteExactlyOnMarker) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 10));
-
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 10, 0);
-
-  EXPECT_EQ(0u, markers.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_InsertInMarkerInterior) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-
-  // insert in middle of second marker
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 7, 0, 5);
-
-  EXPECT_EQ(2u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(15u, markers[1]->StartOffset());
-  EXPECT_EQ(20u, markers[1]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_InsertInMarkerInterior) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-
-  // insert in middle of second marker
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 7, 0, 5);
-
-  EXPECT_EQ(3u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(5u, markers[1]->StartOffset());
-  EXPECT_EQ(15u, markers[1]->EndOffset());
-
-  EXPECT_EQ(15u, markers[2]->StartOffset());
-  EXPECT_EQ(20u, markers[2]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentDependentMarker_InsertBetweenMarkers) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-
-  // insert before second marker
-  DocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 0, 5);
-
-  EXPECT_EQ(3u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(10u, markers[1]->StartOffset());
-  EXPECT_EQ(15u, markers[1]->EndOffset());
-
-  EXPECT_EQ(15u, markers[2]->StartOffset());
-  EXPECT_EQ(20u, markers[2]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       ContentIndependentMarker_InsertBetweenMarkers) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-
-  // insert before second marker
-  DocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 0, 5);
-
-  EXPECT_EQ(3u, markers.size());
-
-  EXPECT_EQ(0u, markers[0]->StartOffset());
-  EXPECT_EQ(5u, markers[0]->EndOffset());
-
-  EXPECT_EQ(10u, markers[1]->StartOffset());
-  EXPECT_EQ(15u, markers[1]->EndOffset());
-
-  EXPECT_EQ(15u, markers[2]->StartOffset());
-  EXPECT_EQ(20u, markers[2]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, FirstMarkerIntersectingRange_Empty) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 10, 15);
-  EXPECT_EQ(nullptr, marker);
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       FirstMarkerIntersectingRange_TouchingAfter) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 5, 10);
-  EXPECT_EQ(nullptr, marker);
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       FirstMarkerIntersectingRange_TouchingBefore) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 0, 5);
-  EXPECT_EQ(nullptr, marker);
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       FirstMarkerIntersectingRange_IntersectingAfter) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 0, 6);
-  EXPECT_NE(nullptr, marker);
-
-  EXPECT_EQ(5u, marker->StartOffset());
-  EXPECT_EQ(10u, marker->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       FirstMarkerIntersectingRange_IntersectingBefore) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 9, 15);
-  EXPECT_NE(nullptr, marker);
-
-  EXPECT_EQ(5u, marker->StartOffset());
-  EXPECT_EQ(10u, marker->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       FirstMarkerIntersectingRange_MultipleMarkers) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-  markers.push_back(CreateMarker(15, 20));
-  markers.push_back(CreateMarker(20, 25));
-
-  DocumentMarker* marker =
-      DocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 7, 17);
-  EXPECT_NE(nullptr, marker);
-
-  EXPECT_EQ(5u, marker->StartOffset());
-  EXPECT_EQ(10u, marker->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, MarkersIntersectingRange_Empty) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 10, 15);
-  EXPECT_EQ(0u, markers_intersecting_range.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest, MarkersIntersectingRange_TouchingAfter) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 5, 10);
-  EXPECT_EQ(0u, markers_intersecting_range.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest, MarkersIntersectingRange_TouchingBefore) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 0, 5);
-  EXPECT_EQ(0u, markers_intersecting_range.size());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       MarkersIntersectingRange_IntersectingAfter) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 0, 6);
-  EXPECT_EQ(1u, markers_intersecting_range.size());
-
-  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
-  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest,
-       MarkersIntersectingRange_IntersectingBefore) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 9, 15);
-  EXPECT_EQ(1u, markers_intersecting_range.size());
-
-  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
-  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, MarkersIntersectingRange_CollapsedRange) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(5, 10));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 7, 7);
-  EXPECT_EQ(1u, markers_intersecting_range.size());
-
-  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
-  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
-}
-
-TEST_F(DocumentMarkerListEditorTest, MarkersIntersectingRange_MultipleMarkers) {
-  DocumentMarkerListEditor::MarkerList markers;
-  markers.push_back(CreateMarker(0, 5));
-  markers.push_back(CreateMarker(5, 10));
-  markers.push_back(CreateMarker(10, 15));
-  markers.push_back(CreateMarker(15, 20));
-  markers.push_back(CreateMarker(20, 25));
-
-  DocumentMarkerListEditor::MarkerList markers_intersecting_range =
-      DocumentMarkerListEditor::MarkersIntersectingRange(markers, 7, 17);
-  EXPECT_EQ(3u, markers_intersecting_range.size());
-
-  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
-  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
-
-  EXPECT_EQ(10u, markers_intersecting_range[1]->StartOffset());
-  EXPECT_EQ(15u, markers_intersecting_range[1]->EndOffset());
-
-  EXPECT_EQ(15u, markers_intersecting_range[2]->StartOffset());
-  EXPECT_EQ(20u, markers_intersecting_range[2]->EndOffset());
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.cpp
similarity index 87%
rename from third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
rename to third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.cpp
index a65f41c..b98934b 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.cpp
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 
 #include "core/editing/markers/SpellCheckMarkerListImpl.h"
 
 namespace blink {
 
-void DocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(
+void SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(
     MarkerList* list,
     DocumentMarker* marker) {
   if (list->IsEmpty() || list->back()->EndOffset() <= marker->StartOffset()) {
@@ -34,9 +34,9 @@
   list->insert(pos - list->begin(), marker);
 }
 
-bool DocumentMarkerListEditor::MoveMarkers(MarkerList* src_list,
-                                           int length,
-                                           DocumentMarkerList* dst_list) {
+bool SortedDocumentMarkerListEditor::MoveMarkers(MarkerList* src_list,
+                                                 int length,
+                                                 DocumentMarkerList* dst_list) {
   DCHECK_GT(length, 0);
   bool didMoveMarker = false;
   unsigned end_offset = length - 1;
@@ -61,9 +61,9 @@
   return didMoveMarker;
 }
 
-bool DocumentMarkerListEditor::RemoveMarkers(MarkerList* list,
-                                             unsigned start_offset,
-                                             int length) {
+bool SortedDocumentMarkerListEditor::RemoveMarkers(MarkerList* list,
+                                                   unsigned start_offset,
+                                                   int length) {
   const unsigned end_offset = start_offset + length;
   MarkerList::iterator start_pos = std::upper_bound(
       list->begin(), list->end(), start_offset,
@@ -81,7 +81,7 @@
   return start_pos != end_pos;
 }
 
-bool DocumentMarkerListEditor::ShiftMarkersContentDependent(
+bool SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(
     MarkerList* list,
     unsigned offset,
     unsigned old_length,
@@ -122,7 +122,7 @@
   return did_shift_marker;
 }
 
-bool DocumentMarkerListEditor::ShiftMarkersContentIndependent(
+bool SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
     MarkerList* list,
     unsigned offset,
     unsigned old_length,
@@ -166,7 +166,7 @@
   return did_shift_marker;
 }
 
-DocumentMarker* DocumentMarkerListEditor::FirstMarkerIntersectingRange(
+DocumentMarker* SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
     const MarkerList& list,
     unsigned start_offset,
     unsigned end_offset) {
@@ -187,9 +187,9 @@
 }
 
 HeapVector<Member<DocumentMarker>>
-DocumentMarkerListEditor::MarkersIntersectingRange(const MarkerList& list,
-                                                   unsigned start_offset,
-                                                   unsigned end_offset) {
+SortedDocumentMarkerListEditor::MarkersIntersectingRange(const MarkerList& list,
+                                                         unsigned start_offset,
+                                                         unsigned end_offset) {
   DCHECK_LE(start_offset, end_offset);
 
   const auto& start_it =
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.h b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.h
similarity index 93%
rename from third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.h
rename to third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.h
index f6aaf30..a224f05 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.h
+++ b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditor.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef DocumentMarkerListEditor_h
-#define DocumentMarkerListEditor_h
+#ifndef SortedDocumentMarkerListEditor_h
+#define SortedDocumentMarkerListEditor_h
 
 #include "core/editing/markers/DocumentMarkerList.h"
 #include "platform/heap/Handle.h"
@@ -12,7 +12,7 @@
 
 class DocumentMarker;
 
-class CORE_EXPORT DocumentMarkerListEditor final {
+class CORE_EXPORT SortedDocumentMarkerListEditor final {
  public:
   using MarkerList = HeapVector<Member<DocumentMarker>>;
 
@@ -61,4 +61,4 @@
 
 }  // namespace blink
 
-#endif  // DocumentMarkerListEditor_h
+#endif  // SortedDocumentMarkerListEditor_h
diff --git a/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditorTest.cpp b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditorTest.cpp
new file mode 100644
index 0000000..ba952032
--- /dev/null
+++ b/third_party/WebKit/Source/core/editing/markers/SortedDocumentMarkerListEditorTest.cpp
@@ -0,0 +1,600 @@
+// 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 "core/editing/markers/SortedDocumentMarkerListEditor.h"
+
+#include "core/editing/markers/TextMatchMarker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class SortedDocumentMarkerListEditorTest : public ::testing::Test {
+ protected:
+  DocumentMarker* CreateMarker(unsigned startOffset, unsigned endOffset) {
+    return new TextMatchMarker(startOffset, endOffset,
+                               TextMatchMarker::MatchStatus::kInactive);
+  }
+};
+
+TEST_F(SortedDocumentMarkerListEditorTest, RemoveMarkersEmptyList) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  SortedDocumentMarkerListEditor::RemoveMarkers(&markers, 0, 10);
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest, RemoveMarkersTouchingEndpoints) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+  markers.push_back(CreateMarker(10, 20));
+  markers.push_back(CreateMarker(20, 30));
+
+  SortedDocumentMarkerListEditor::RemoveMarkers(&markers, 10, 10);
+
+  EXPECT_EQ(2u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  EXPECT_EQ(20u, markers[1]->StartOffset());
+  EXPECT_EQ(30u, markers[1]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       RemoveMarkersOneCharacterIntoInterior) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+  markers.push_back(CreateMarker(10, 20));
+  markers.push_back(CreateMarker(20, 30));
+
+  SortedDocumentMarkerListEditor::RemoveMarkers(&markers, 9, 12);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceStartOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 5,
+                                                               5);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceStartOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 5,
+                                                                 4);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 4,
+                                                                 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 5,
+                                                                 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceContainsStartOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10,
+                                                               10);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceContainsStartOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(10u, markers[0]->StartOffset());
+  EXPECT_EQ(15u, markers[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceEndOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 5,
+                                                               5);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceEndOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 5,
+                                                                 4);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 4,
+                                                                 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 5,
+                                                                 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceContainsEndOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 10,
+                                                               10);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceContainsEndOfMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                 10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceEntireMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10,
+                                                               10);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceEntireMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 10, 9);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0, 9,
+                                                                 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceTextWithMarkerAtBeginning) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 15,
+                                                               15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtBeginning) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 15, 15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_ReplaceTextWithMarkerAtEnd) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 15,
+                                                               15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtEnd) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 15, 15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest, ContentDependentMarker_Deletions) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+  markers.push_back(CreateMarker(15, 20));
+  markers.push_back(CreateMarker(20, 25));
+
+  // Delete range containing the end of the second marker, the entire third
+  // marker, and the start of the fourth marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 8, 9,
+                                                               0);
+
+  EXPECT_EQ(2u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(11u, markers[1]->StartOffset());
+  EXPECT_EQ(16u, markers[1]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest, ContentIndependentMarker_Deletions) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+  markers.push_back(CreateMarker(15, 20));
+  markers.push_back(CreateMarker(20, 25));
+
+  // Delete range containing the end of the second marker, the entire third
+  // marker, and the start of the fourth marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 8, 9,
+                                                                 0);
+
+  EXPECT_EQ(4u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(5u, markers[1]->StartOffset());
+  EXPECT_EQ(8u, markers[1]->EndOffset());
+
+  EXPECT_EQ(8u, markers[2]->StartOffset());
+  EXPECT_EQ(11u, markers[2]->EndOffset());
+
+  EXPECT_EQ(11u, markers[3]->StartOffset());
+  EXPECT_EQ(16u, markers[3]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_DeleteExactlyOnMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 0, 10,
+                                                               0);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_DeleteExactlyOnMarker) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                 10, 0);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_InsertInMarkerInterior) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert in middle of second marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 7, 0,
+                                                               5);
+
+  EXPECT_EQ(2u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(15u, markers[1]->StartOffset());
+  EXPECT_EQ(20u, markers[1]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_InsertInMarkerInterior) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert in middle of second marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 7, 0,
+                                                                 5);
+
+  EXPECT_EQ(3u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(5u, markers[1]->StartOffset());
+  EXPECT_EQ(15u, markers[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers[2]->StartOffset());
+  EXPECT_EQ(20u, markers[2]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentDependentMarker_InsertBetweenMarkers) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert before second marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(&markers, 5, 0,
+                                                               5);
+
+  EXPECT_EQ(3u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(10u, markers[1]->StartOffset());
+  EXPECT_EQ(15u, markers[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers[2]->StartOffset());
+  EXPECT_EQ(20u, markers[2]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_InsertBetweenMarkers) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert before second marker
+  SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5, 0,
+                                                                 5);
+
+  EXPECT_EQ(3u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(10u, markers[1]->StartOffset());
+  EXPECT_EQ(15u, markers[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers[2]->StartOffset());
+  EXPECT_EQ(20u, markers[2]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest, FirstMarkerIntersectingRange_Empty) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 10,
+                                                                   15);
+  EXPECT_EQ(nullptr, marker);
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       FirstMarkerIntersectingRange_TouchingAfter) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 5,
+                                                                   10);
+  EXPECT_EQ(nullptr, marker);
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       FirstMarkerIntersectingRange_TouchingBefore) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 0,
+                                                                   5);
+  EXPECT_EQ(nullptr, marker);
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       FirstMarkerIntersectingRange_IntersectingAfter) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 0,
+                                                                   6);
+  EXPECT_NE(nullptr, marker);
+
+  EXPECT_EQ(5u, marker->StartOffset());
+  EXPECT_EQ(10u, marker->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       FirstMarkerIntersectingRange_IntersectingBefore) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 9,
+                                                                   15);
+  EXPECT_NE(nullptr, marker);
+
+  EXPECT_EQ(5u, marker->StartOffset());
+  EXPECT_EQ(10u, marker->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       FirstMarkerIntersectingRange_MultipleMarkers) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+  markers.push_back(CreateMarker(15, 20));
+  markers.push_back(CreateMarker(20, 25));
+
+  DocumentMarker* marker =
+      SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(markers, 7,
+                                                                   17);
+  EXPECT_NE(nullptr, marker);
+
+  EXPECT_EQ(5u, marker->StartOffset());
+  EXPECT_EQ(10u, marker->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest, MarkersIntersectingRange_Empty) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 10, 15);
+  EXPECT_EQ(0u, markers_intersecting_range.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_TouchingAfter) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 5, 10);
+  EXPECT_EQ(0u, markers_intersecting_range.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_TouchingBefore) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 0, 5);
+  EXPECT_EQ(0u, markers_intersecting_range.size());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_IntersectingAfter) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 0, 6);
+  EXPECT_EQ(1u, markers_intersecting_range.size());
+
+  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
+  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_IntersectingBefore) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 9, 15);
+  EXPECT_EQ(1u, markers_intersecting_range.size());
+
+  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
+  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_CollapsedRange) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 10));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 7, 7);
+  EXPECT_EQ(1u, markers_intersecting_range.size());
+
+  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
+  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
+}
+
+TEST_F(SortedDocumentMarkerListEditorTest,
+       MarkersIntersectingRange_MultipleMarkers) {
+  SortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+  markers.push_back(CreateMarker(15, 20));
+  markers.push_back(CreateMarker(20, 25));
+
+  SortedDocumentMarkerListEditor::MarkerList markers_intersecting_range =
+      SortedDocumentMarkerListEditor::MarkersIntersectingRange(markers, 7, 17);
+  EXPECT_EQ(3u, markers_intersecting_range.size());
+
+  EXPECT_EQ(5u, markers_intersecting_range[0]->StartOffset());
+  EXPECT_EQ(10u, markers_intersecting_range[0]->EndOffset());
+
+  EXPECT_EQ(10u, markers_intersecting_range[1]->StartOffset());
+  EXPECT_EQ(15u, markers_intersecting_range[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers_intersecting_range[2]->StartOffset());
+  EXPECT_EQ(20u, markers_intersecting_range[2]->EndOffset());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
index df924c1..5a1ec40 100644
--- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
@@ -4,7 +4,7 @@
 
 #include "core/editing/markers/SpellCheckMarkerListImpl.h"
 
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 
 namespace blink {
 
@@ -69,33 +69,34 @@
 DocumentMarker* SpellCheckMarkerListImpl::FirstMarkerIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return DocumentMarkerListEditor::FirstMarkerIntersectingRange(
+  return SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 HeapVector<Member<DocumentMarker>>
 SpellCheckMarkerListImpl::MarkersIntersectingRange(unsigned start_offset,
                                                    unsigned end_offset) const {
-  return DocumentMarkerListEditor::MarkersIntersectingRange(
+  return SortedDocumentMarkerListEditor::MarkersIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 bool SpellCheckMarkerListImpl::MoveMarkers(int length,
                                            DocumentMarkerList* dst_list) {
-  return DocumentMarkerListEditor::MoveMarkers(&markers_, length, dst_list);
+  return SortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
+                                                     dst_list);
 }
 
 bool SpellCheckMarkerListImpl::RemoveMarkers(unsigned start_offset,
                                              int length) {
-  return DocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
-                                                 length);
+  return SortedDocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
+                                                       length);
 }
 
 bool SpellCheckMarkerListImpl::ShiftMarkers(const String&,
                                             unsigned offset,
                                             unsigned old_length,
                                             unsigned new_length) {
-  return DocumentMarkerListEditor::ShiftMarkersContentDependent(
+  return SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(
       &markers_, offset, old_length, new_length);
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
index ae31e37..419bda24 100644
--- a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
@@ -4,7 +4,7 @@
 
 #include "core/editing/markers/SuggestionMarkerListImpl.h"
 
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 #include "core/editing/markers/SuggestionMarker.h"
 #include "core/editing/markers/SuggestionMarkerReplacementScope.h"
 
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
index 1db3b60a..ddc337dc 100644
--- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
@@ -7,7 +7,7 @@
 #include "core/dom/Node.h"
 #include "core/dom/Range.h"
 #include "core/editing/EphemeralRange.h"
-#include "core/editing/markers/DocumentMarkerListEditor.h"
+#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
 #include "core/editing/markers/TextMatchMarker.h"
 #include "third_party/WebKit/Source/core/editing/VisibleUnits.h"
 
@@ -22,8 +22,8 @@
 }
 
 void TextMatchMarkerListImpl::Add(DocumentMarker* marker) {
-  DocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
-                                                               marker);
+  SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
+                                                                     marker);
 }
 
 void TextMatchMarkerListImpl::Clear() {
@@ -38,32 +38,33 @@
 DocumentMarker* TextMatchMarkerListImpl::FirstMarkerIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return DocumentMarkerListEditor::FirstMarkerIntersectingRange(
+  return SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 HeapVector<Member<DocumentMarker>>
 TextMatchMarkerListImpl::MarkersIntersectingRange(unsigned start_offset,
                                                   unsigned end_offset) const {
-  return DocumentMarkerListEditor::MarkersIntersectingRange(
+  return SortedDocumentMarkerListEditor::MarkersIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 bool TextMatchMarkerListImpl::MoveMarkers(int length,
                                           DocumentMarkerList* dst_list) {
-  return DocumentMarkerListEditor::MoveMarkers(&markers_, length, dst_list);
+  return SortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
+                                                     dst_list);
 }
 
 bool TextMatchMarkerListImpl::RemoveMarkers(unsigned start_offset, int length) {
-  return DocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
-                                                 length);
+  return SortedDocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
+                                                       length);
 }
 
 bool TextMatchMarkerListImpl::ShiftMarkers(const String&,
                                            unsigned offset,
                                            unsigned old_length,
                                            unsigned new_length) {
-  return DocumentMarkerListEditor::ShiftMarkersContentDependent(
+  return SortedDocumentMarkerListEditor::ShiftMarkersContentDependent(
       &markers_, offset, old_length, new_length);
 }
 
diff --git a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
index 354a374..ed0120c 100644
--- a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
@@ -103,6 +103,7 @@
 #include "platform/bindings/Microtask.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/graphics/GraphicsLayer.h"
+#include "platform/graphics/compositing/PaintArtifactCompositor.h"
 #include "platform/loader/fetch/FetchParameters.h"
 #include "platform/loader/fetch/MemoryCache.h"
 #include "platform/loader/fetch/ResourceError.h"
@@ -11678,7 +11679,6 @@
   tester.ExpectTotalCount(histogramName, 3);
 }
 
-// TODO(pdr): Create a version of this test for SPV2 (crbug.com.758028).
 TEST_P(ParameterizedWebFrameTest, DidScrollCallbackAfterScrollableAreaChanges) {
   FrameTestHelpers::WebViewHelper web_view_helper;
   web_view_helper.Initialize();
@@ -11713,6 +11713,8 @@
       scrollable_area->LayerForScrolling()->PlatformLayer();
   EXPECT_NE(nullptr, web_scroll_layer);
 
+  // Ensure a synthetic impl-side scroll offset propagates to the scrollable
+  // area using the DidScroll callback.
   EXPECT_EQ(ScrollOffset(), scrollable_area->GetScrollOffset());
   web_scroll_layer->SetScrollOffsetFromImplSideForTesting(
       gfx::ScrollOffset(0, 1));
@@ -11737,4 +11739,124 @@
       gfx::ScrollOffset(0, 3));
 }
 
+class SlimmingPaintWebFrameTest
+    : public ::testing::WithParamInterface<TestParamRootLayerScrolling>,
+      private ScopedRootLayerScrollingForTest,
+      private ScopedSlimmingPaintV2ForTest,
+      public WebFrameTest {
+ public:
+  SlimmingPaintWebFrameTest()
+      : ScopedRootLayerScrollingForTest(GetParam()),
+        ScopedSlimmingPaintV2ForTest(true) {}
+
+  void SetUp() override {
+    web_view_helper_ = WTF::MakeUnique<FrameTestHelpers::WebViewHelper>();
+    web_view_helper_->Initialize(nullptr, &web_view_client_, nullptr,
+                                 &ConfigureCompositingWebView);
+    web_view_helper_->Resize(WebSize(200, 200));
+
+    // The paint artifact compositor should have been created as part of the
+    // web view helper setup.
+    DCHECK(paint_artifact_compositor());
+    paint_artifact_compositor()->EnableExtraDataForTesting();
+  }
+
+  WebLocalFrame* LocalMainFrame() { return web_view_helper_->LocalMainFrame(); }
+
+  WebViewImpl* WebView() { return web_view_helper_->WebView(); }
+
+  size_t ContentLayerCount() {
+    return paint_artifact_compositor()
+        ->GetExtraDataForTesting()
+        ->content_layers.size();
+  }
+
+  size_t ScrollHitTestLayerCount() {
+    return paint_artifact_compositor()
+        ->GetExtraDataForTesting()
+        ->scroll_hit_test_layers.size();
+  }
+
+  std::unique_ptr<WebLayer> ContentLayerAt(unsigned index) {
+    return paint_artifact_compositor()
+        ->GetExtraDataForTesting()
+        ->ContentWebLayerAt(index);
+  }
+
+  std::unique_ptr<WebLayer> ScrollHitTestLayerAt(unsigned index) {
+    return paint_artifact_compositor()
+        ->GetExtraDataForTesting()
+        ->ScrollHitTestWebLayerAt(index);
+  }
+
+ private:
+  PaintArtifactCompositor* paint_artifact_compositor() {
+    auto* frame_view = web_view_helper_->LocalMainFrame()->GetFrameView();
+    return frame_view->GetPaintArtifactCompositorForTesting();
+  }
+  FrameTestHelpers::TestWebViewClient web_view_client_;
+  std::unique_ptr<FrameTestHelpers::WebViewHelper> web_view_helper_;
+};
+
+INSTANTIATE_TEST_CASE_P(All, SlimmingPaintWebFrameTest, ::testing::Bool());
+
+TEST_P(SlimmingPaintWebFrameTest, DidScrollCallbackAfterScrollableAreaChanges) {
+  DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+
+  InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(),
+                     "<style>"
+                     "  #scrollable {"
+                     "    height: 100px;"
+                     "    width: 100px;"
+                     "    overflow: scroll;"
+                     "    will-change: transform;"
+                     "  }"
+                     "  #forceScroll { height: 120px; width: 50px; }"
+                     "</style>"
+                     "<div id='scrollable'>"
+                     "  <div id='forceScroll'></div>"
+                     "</div>");
+
+  WebView()->UpdateAllLifecyclePhases();
+
+  Document* document = WebView()->MainFrameImpl()->GetFrame()->GetDocument();
+  Element* scrollable = document->getElementById("scrollable");
+
+  auto* scrollable_area =
+      ToLayoutBox(scrollable->GetLayoutObject())->GetScrollableArea();
+  EXPECT_NE(nullptr, scrollable_area);
+
+  EXPECT_EQ(ContentLayerCount(), 2u);
+  EXPECT_EQ(ScrollHitTestLayerCount(), 1u);
+
+  // Ensure a synthetic impl-side scroll offset propagates to the scrollable
+  // area using the DidScroll callback.
+  EXPECT_EQ(ScrollOffset(), scrollable_area->GetScrollOffset());
+  ScrollHitTestLayerAt(0)->SetScrollOffsetFromImplSideForTesting(
+      gfx::ScrollOffset(0, 1));
+  WebView()->UpdateAllLifecyclePhases();
+  EXPECT_EQ(ScrollOffset(0, 1), scrollable_area->GetScrollOffset());
+
+  // Make the scrollable area non-scrollable.
+  scrollable->setAttribute(HTMLNames::styleAttr, "overflow: visible");
+
+  // Update layout without updating compositing state.
+  LocalMainFrame()->ExecuteScript(
+      WebScriptSource("var forceLayoutFromScript = scrollable.offsetTop;"));
+  EXPECT_EQ(document->Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
+
+  EXPECT_EQ(nullptr,
+            ToLayoutBox(scrollable->GetLayoutObject())->GetScrollableArea());
+
+  // The web scroll layer has not been deleted yet and we should be able to
+  // apply impl-side offsets without crashing.
+  EXPECT_EQ(ScrollHitTestLayerCount(), 1u);
+  ScrollHitTestLayerAt(0)->SetScrollOffsetFromImplSideForTesting(
+      gfx::ScrollOffset(0, 3));
+
+  WebView()->UpdateAllLifecyclePhases();
+  EXPECT_EQ(ContentLayerCount(), 1u);
+  EXPECT_EQ(ScrollHitTestLayerCount(), 0u);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index 4b5997d3..471714f 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -853,6 +853,11 @@
   // LocalFrameView (or possibly the LocalFrameView itself).
   ScrollableArea* ScrollableAreaWithElementId(const CompositorElementId&);
 
+  PaintArtifactCompositor* GetPaintArtifactCompositorForTesting() {
+    DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+    return paint_artifact_compositor_.get();
+  }
+
  protected:
   // Scroll the content via the compositor.
   bool ScrollContentsFastPath(const IntSize& scroll_delta);
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 058da4b9..d16fe4b 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -2223,11 +2223,10 @@
 
   // The spec does not define an invalid value default:
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28950
-
-  // TODO(foolip): Try to make "metadata" the default preload state:
-  // https://crbug.com/310450
   UseCounter::Count(GetDocument(), WebFeature::kHTMLMediaElementPreloadDefault);
-  return WebMediaPlayer::kPreloadAuto;
+  return RuntimeEnabledFeatures::PreloadDefaultIsMetadataEnabled()
+             ? WebMediaPlayer::kPreloadMetaData
+             : WebMediaPlayer::kPreloadAuto;
 }
 
 String HTMLMediaElement::EffectivePreload() const {
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
index e76e744..ca214541 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
@@ -114,8 +114,8 @@
       {false, false, true, TestURLScheme::kHttp, "auto", "metadata"},
       {false, false, true, TestURLScheme::kHttp, "scheme", "metadata"},
       {false, false, true, TestURLScheme::kHttp, "none", "none"},
-      // Tests that the preload is overriden to "auto"
-      {false, false, false, TestURLScheme::kHttp, "foo", "auto"},
+      // Tests that the preload is overriden to "metadata".
+      {false, false, false, TestURLScheme::kHttp, "foo", "metadata"},
   };
 
   int index = 0;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index d818073b..b687b078 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -5100,7 +5100,7 @@
       TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
                    zoffset, format, type,
                    canvas
-                       ->CopiedImage(kFrontBuffer, kPreferAcceleration,
+                       ->CopiedImage(kBackBuffer, kPreferAcceleration,
                                      FunctionIDToSnapshotReason(function_id))
                        .Get(),
                    WebGLImageConversion::kHtmlDomCanvas, unpack_flip_y_,
@@ -5134,7 +5134,7 @@
     TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
                  zoffset, format, type,
                  canvas
-                     ->CopiedImage(kFrontBuffer, kPreferAcceleration,
+                     ->CopiedImage(kBackBuffer, kPreferAcceleration,
                                    FunctionIDToSnapshotReason(function_id))
                      .Get(),
                  WebGLImageConversion::kHtmlDomCanvas, unpack_flip_y_,
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index ae8c81a..4c10cdc 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -853,6 +853,10 @@
       status: "stable",
     },
     {
+      name: "PreloadDefaultIsMetadata",
+      status: "experimental",
+    },
+    {
       name: "Presentation",
       status: "stable",
     },
diff --git a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
index 13153174..506a84f7 100644
--- a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
@@ -248,6 +248,10 @@
   RuntimeEnabledFeatures::SetPermissionsEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnablePreloadDefaultIsMetadata(bool enable) {
+  RuntimeEnabledFeatures::SetPreloadDefaultIsMetadataEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableScriptedSpeech(bool enable) {
   RuntimeEnabledFeatures::SetScriptedSpeechEnabled(enable);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index badb2a7..eafaa72 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -754,6 +754,19 @@
   }
 }
 
+std::unique_ptr<WebLayer>
+PaintArtifactCompositor::ExtraDataForTesting::ContentWebLayerAt(
+    unsigned index) {
+  return Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+      content_layers[index].get());
+}
+std::unique_ptr<WebLayer>
+PaintArtifactCompositor::ExtraDataForTesting::ScrollHitTestWebLayerAt(
+    unsigned index) {
+  return Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+      scroll_hit_test_layers[index].get());
+}
+
 #ifndef NDEBUG
 void PaintArtifactCompositor::ShowDebugData() {
   LOG(ERROR) << LayersAsJSON(kLayerTreeIncludesDebugInfo)
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
index 0ce1663..d343547 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
@@ -77,6 +77,9 @@
   // way of locating the layers of interest, since there are still a slew of
   // placeholder layers required.
   struct ExtraDataForTesting {
+    std::unique_ptr<WebLayer> ContentWebLayerAt(unsigned index);
+    std::unique_ptr<WebLayer> ScrollHitTestWebLayerAt(unsigned index);
+
     Vector<scoped_refptr<cc::Layer>> content_layers;
     Vector<scoped_refptr<cc::Layer>> synthesized_clip_layers;
     Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers;
diff --git a/third_party/WebKit/public/platform/WebRuntimeFeatures.h b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
index a9c0596f..aac83b5 100644
--- a/third_party/WebKit/public/platform/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
@@ -113,6 +113,7 @@
   BLINK_PLATFORM_EXPORT static void EnablePaymentRequest(bool);
   BLINK_PLATFORM_EXPORT static void EnablePermissionsAPI(bool);
   BLINK_PLATFORM_EXPORT static void EnablePreciseMemoryInfo(bool);
+  BLINK_PLATFORM_EXPORT static void EnablePreloadDefaultIsMetadata(bool);
   BLINK_PLATFORM_EXPORT static void EnablePrintBrowser(bool);
   BLINK_PLATFORM_EXPORT static void EnablePresentationAPI(bool);
   BLINK_PLATFORM_EXPORT static void EnablePushMessaging(bool);
diff --git a/third_party/android_support_test_runner/rules_java.info b/third_party/android_support_test_runner/rules_java.info
new file mode 100644
index 0000000..28c8330
--- /dev/null
+++ b/third_party/android_support_test_runner/rules_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/third_party/closure_compiler/externs/networking_private.js b/third_party/closure_compiler/externs/networking_private.js
index 961262a..4e63c9e 100644
--- a/third_party/closure_compiler/externs/networking_private.js
+++ b/third_party/closure_compiler/externs/networking_private.js
@@ -999,8 +999,8 @@
 /**
  * @typedef {{
  *   Scanning: (boolean|undefined),
- *   SimLockType: (string|undefined),
- *   SimPresent: (boolean|undefined),
+ *   SIMLockStatus: (!chrome.networkingPrivate.SIMLockStatus|undefined),
+ *   SIMPresent: (boolean|undefined),
  *   State: !chrome.networkingPrivate.DeviceStateType,
  *   Type: !chrome.networkingPrivate.NetworkType
  * }}
diff --git a/third_party/gvr-android-sdk/controller_test_api_java.info b/third_party/gvr-android-sdk/controller_test_api_java.info
new file mode 100644
index 0000000..a2ebd4a
--- /dev/null
+++ b/third_party/gvr-android-sdk/controller_test_api_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/third_party/gvr-android-sdk/gvr_common_java.info b/third_party/gvr-android-sdk/gvr_common_java.info
new file mode 100644
index 0000000..aacf1d9
--- /dev/null
+++ b/third_party/gvr-android-sdk/gvr_common_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = true
+is_manifest_empty = true
+resources = [ "res/drawable-hdpi-v4/quantum_ic_close_white_24.png", "res/drawable-hdpi-v4/quantum_ic_settings_white_24.png", "res/drawable-hdpi-v4/transition.png", "res/drawable-mdpi-v4/quantum_ic_close_white_24.png", "res/drawable-mdpi-v4/quantum_ic_settings_white_24.png", "res/drawable-mdpi-v4/transition.png", "res/drawable-v21/rippleable.xml", "res/drawable-xhdpi-v4/quantum_ic_close_white_24.png", "res/drawable-xhdpi-v4/quantum_ic_settings_white_24.png", "res/drawable-xxhdpi-v4/quantum_ic_close_white_24.png", "res/drawable-xxhdpi-v4/quantum_ic_settings_white_24.png", "res/drawable-xxxhdpi-v4/quantum_ic_close_white_24.png", "res/drawable-xxxhdpi-v4/quantum_ic_settings_white_24.png", "res/drawable/rippleable.xml", "res/layout-land/back_button.xml", "res/layout-land/settings_button.xml", "res/layout-land/ui_layer_with_portrait_support.xml", "res/layout-ldrtl-land-v17/back_button.xml", "res/layout-ldrtl-land-v17/settings_button.xml", "res/layout-ldrtl-v17/back_button.xml", "res/layout-ldrtl-v17/settings_button.xml", "res/layout/back_button.xml", "res/layout/settings_button.xml", "res/layout/transition_view.xml", "res/layout/ui_layer.xml", "res/layout/ui_layer_with_portrait_support.xml", "res/values-ar/values.xml", "res/values-bg/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rGB/values.xml", "res/values-es-rUS/values.xml", "res/values-es/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-id/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ko/values.xml", "res/values-land/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-nl/values.xml", "res/values-no/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-v19/values.xml", "res/values-v21/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rTW/values.xml", "res/values/values.xml" ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/third_party/gvr-android-sdk/gvr_controller_java.info b/third_party/gvr-android-sdk/gvr_controller_java.info
new file mode 100644
index 0000000..a2ebd4a
--- /dev/null
+++ b/third_party/gvr-android-sdk/gvr_controller_java.info
@@ -0,0 +1,13 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = [  ]
+assets = [  ]
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = false
+has_r_text_file = false
+is_manifest_empty = true
+resources = [  ]
+subjar_tuples = [  ]
+subjars = [  ]
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 6ed060d9..6093bfc 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Tuesday August 15 2017
+Date: Wednesday August 23 2017
 Branch: master
-Commit: 6b9c691dafc884424e05b5294b6e0e4996d533a5
+Commit: 30c261b1ebe8f06d687cac5b3b442d51a7839d00
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/libvpx_srcs.gni b/third_party/libvpx/libvpx_srcs.gni
index 53d01a5..4ac157ee 100644
--- a/third_party/libvpx/libvpx_srcs.gni
+++ b/third_party/libvpx/libvpx_srcs.gni
@@ -444,10 +444,14 @@
 libvpx_srcs_x86_sse4_1 = [
   "//third_party/libvpx/source/libvpx/vp8/encoder/x86/quantize_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct16x16_add_sse4.c",
+  "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct32x32_add_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct4x4_add_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct8x8_add_sse4.c",
 ]
-libvpx_srcs_x86_avx = [ "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_diamond_search_sad_avx.c" ]
+libvpx_srcs_x86_avx = [
+  "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_diamond_search_sad_avx.c",
+  "//third_party/libvpx/source/libvpx/vpx_dsp/x86/quantize_avx.c",
+]
 libvpx_srcs_x86_avx2 = [
   "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_error_avx2.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/fwd_txfm_avx2.c",
@@ -906,10 +910,14 @@
 libvpx_srcs_x86_64_sse4_1 = [
   "//third_party/libvpx/source/libvpx/vp8/encoder/x86/quantize_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct16x16_add_sse4.c",
+  "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct32x32_add_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct4x4_add_sse4.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/highbd_idct8x8_add_sse4.c",
 ]
-libvpx_srcs_x86_64_avx = [ "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_diamond_search_sad_avx.c" ]
+libvpx_srcs_x86_64_avx = [
+  "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_diamond_search_sad_avx.c",
+  "//third_party/libvpx/source/libvpx/vpx_dsp/x86/quantize_avx.c",
+]
 libvpx_srcs_x86_64_avx2 = [
   "//third_party/libvpx/source/libvpx/vp9/encoder/x86/vp9_error_avx2.c",
   "//third_party/libvpx/source/libvpx/vpx_dsp/x86/fwd_txfm_avx2.c",
@@ -2789,6 +2797,7 @@
   "//third_party/libvpx/source/libvpx/vpx_mem/include/vpx_mem_intrnl.h",
   "//third_party/libvpx/source/libvpx/vpx_mem/vpx_mem.c",
   "//third_party/libvpx/source/libvpx/vpx_mem/vpx_mem.h",
+  "//third_party/libvpx/source/libvpx/vpx_ports/asmdefs_mmi.h",
   "//third_party/libvpx/source/libvpx/vpx_ports/bitops.h",
   "//third_party/libvpx/source/libvpx/vpx_ports/emmintrin_compat.h",
   "//third_party/libvpx/source/libvpx/vpx_ports/mem.h",
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
index 8eedae1..8cd3c19 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
@@ -199,7 +199,18 @@
                              uint16_t* eob_ptr,
                              const int16_t* scan,
                              const int16_t* iscan);
-#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_c
+void vp9_quantize_fp_32x32_neon(const tran_low_t* coeff_ptr,
+                                intptr_t n_coeffs,
+                                int skip_block,
+                                const int16_t* round_ptr,
+                                const int16_t* quant_ptr,
+                                tran_low_t* qcoeff_ptr,
+                                tran_low_t* dqcoeff_ptr,
+                                const int16_t* dequant_ptr,
+                                uint16_t* eob_ptr,
+                                const int16_t* scan,
+                                const int16_t* iscan);
+#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_neon
 
 void vp9_scale_and_extend_frame_c(const struct yv12_buffer_config* src,
                                   struct yv12_buffer_config* dst,
diff --git a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
index 8eedae1..8cd3c19 100644
--- a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
@@ -199,7 +199,18 @@
                              uint16_t* eob_ptr,
                              const int16_t* scan,
                              const int16_t* iscan);
-#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_c
+void vp9_quantize_fp_32x32_neon(const tran_low_t* coeff_ptr,
+                                intptr_t n_coeffs,
+                                int skip_block,
+                                const int16_t* round_ptr,
+                                const int16_t* quant_ptr,
+                                tran_low_t* qcoeff_ptr,
+                                tran_low_t* dqcoeff_ptr,
+                                const int16_t* dequant_ptr,
+                                uint16_t* eob_ptr,
+                                const int16_t* scan,
+                                const int16_t* iscan);
+#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_neon
 
 void vp9_scale_and_extend_frame_c(const struct yv12_buffer_config* src,
                                   struct yv12_buffer_config* dst,
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
index 4c485fb8..7a339e5 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
@@ -237,7 +237,28 @@
                              uint16_t* eob_ptr,
                              const int16_t* scan,
                              const int16_t* iscan);
-#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_c
+void vp9_quantize_fp_32x32_neon(const tran_low_t* coeff_ptr,
+                                intptr_t n_coeffs,
+                                int skip_block,
+                                const int16_t* round_ptr,
+                                const int16_t* quant_ptr,
+                                tran_low_t* qcoeff_ptr,
+                                tran_low_t* dqcoeff_ptr,
+                                const int16_t* dequant_ptr,
+                                uint16_t* eob_ptr,
+                                const int16_t* scan,
+                                const int16_t* iscan);
+RTCD_EXTERN void (*vp9_quantize_fp_32x32)(const tran_low_t* coeff_ptr,
+                                          intptr_t n_coeffs,
+                                          int skip_block,
+                                          const int16_t* round_ptr,
+                                          const int16_t* quant_ptr,
+                                          tran_low_t* qcoeff_ptr,
+                                          tran_low_t* dqcoeff_ptr,
+                                          const int16_t* dequant_ptr,
+                                          uint16_t* eob_ptr,
+                                          const int16_t* scan,
+                                          const int16_t* iscan);
 
 void vp9_scale_and_extend_frame_c(const struct yv12_buffer_config* src,
                                   struct yv12_buffer_config* dst,
@@ -274,6 +295,9 @@
   vp9_quantize_fp = vp9_quantize_fp_c;
   if (flags & HAS_NEON)
     vp9_quantize_fp = vp9_quantize_fp_neon;
+  vp9_quantize_fp_32x32 = vp9_quantize_fp_32x32_c;
+  if (flags & HAS_NEON)
+    vp9_quantize_fp_32x32 = vp9_quantize_fp_32x32_neon;
 }
 #endif
 
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
index 8eedae1..8cd3c19 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
@@ -199,7 +199,18 @@
                              uint16_t* eob_ptr,
                              const int16_t* scan,
                              const int16_t* iscan);
-#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_c
+void vp9_quantize_fp_32x32_neon(const tran_low_t* coeff_ptr,
+                                intptr_t n_coeffs,
+                                int skip_block,
+                                const int16_t* round_ptr,
+                                const int16_t* quant_ptr,
+                                tran_low_t* qcoeff_ptr,
+                                tran_low_t* dqcoeff_ptr,
+                                const int16_t* dequant_ptr,
+                                uint16_t* eob_ptr,
+                                const int16_t* scan,
+                                const int16_t* iscan);
+#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_neon
 
 void vp9_scale_and_extend_frame_c(const struct yv12_buffer_config* src,
                                   struct yv12_buffer_config* dst,
diff --git a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
index 8eedae1..8cd3c19 100644
--- a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
@@ -199,7 +199,18 @@
                              uint16_t* eob_ptr,
                              const int16_t* scan,
                              const int16_t* iscan);
-#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_c
+void vp9_quantize_fp_32x32_neon(const tran_low_t* coeff_ptr,
+                                intptr_t n_coeffs,
+                                int skip_block,
+                                const int16_t* round_ptr,
+                                const int16_t* quant_ptr,
+                                tran_low_t* qcoeff_ptr,
+                                tran_low_t* dqcoeff_ptr,
+                                const int16_t* dequant_ptr,
+                                uint16_t* eob_ptr,
+                                const int16_t* scan,
+                                const int16_t* iscan);
+#define vp9_quantize_fp_32x32 vp9_quantize_fp_32x32_neon
 
 void vp9_scale_and_extend_frame_c(const struct yv12_buffer_config* src,
                                   struct yv12_buffer_config* dst,
diff --git a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
index 90eff41..10f0c335 100644
--- a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
@@ -4345,13 +4345,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -4370,7 +4392,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -6089,6 +6122,19 @@
                           uint16_t* eob_ptr,
                           const int16_t* scan,
                           const int16_t* iscan);
+void vpx_quantize_b_avx(const tran_low_t* coeff_ptr,
+                        intptr_t n_coeffs,
+                        int skip_block,
+                        const int16_t* zbin_ptr,
+                        const int16_t* round_ptr,
+                        const int16_t* quant_ptr,
+                        const int16_t* quant_shift_ptr,
+                        tran_low_t* qcoeff_ptr,
+                        tran_low_t* dqcoeff_ptr,
+                        const int16_t* dequant_ptr,
+                        uint16_t* eob_ptr,
+                        const int16_t* scan,
+                        const int16_t* iscan);
 RTCD_EXTERN void (*vpx_quantize_b)(const tran_low_t* coeff_ptr,
                                    intptr_t n_coeffs,
                                    int skip_block,
@@ -8956,9 +9002,24 @@
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
   vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_sse2;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
@@ -9280,6 +9341,8 @@
     vpx_quantize_b = vpx_quantize_b_sse2;
   if (flags & HAS_SSSE3)
     vpx_quantize_b = vpx_quantize_b_ssse3;
+  if (flags & HAS_AVX)
+    vpx_quantize_b = vpx_quantize_b_avx;
   vpx_sad16x16 = vpx_sad16x16_c;
   if (flags & HAS_SSE2)
     vpx_sad16x16 = vpx_sad16x16_sse2;
diff --git a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
index 5aa8e2e..76174db 100644
--- a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
@@ -3695,13 +3695,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -3717,7 +3739,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -7218,6 +7251,15 @@
   vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse4_1;
diff --git a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
index 90eff41..10f0c335 100644
--- a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
@@ -4345,13 +4345,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -4370,7 +4392,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -6089,6 +6122,19 @@
                           uint16_t* eob_ptr,
                           const int16_t* scan,
                           const int16_t* iscan);
+void vpx_quantize_b_avx(const tran_low_t* coeff_ptr,
+                        intptr_t n_coeffs,
+                        int skip_block,
+                        const int16_t* zbin_ptr,
+                        const int16_t* round_ptr,
+                        const int16_t* quant_ptr,
+                        const int16_t* quant_shift_ptr,
+                        tran_low_t* qcoeff_ptr,
+                        tran_low_t* dqcoeff_ptr,
+                        const int16_t* dequant_ptr,
+                        uint16_t* eob_ptr,
+                        const int16_t* scan,
+                        const int16_t* iscan);
 RTCD_EXTERN void (*vpx_quantize_b)(const tran_low_t* coeff_ptr,
                                    intptr_t n_coeffs,
                                    int skip_block,
@@ -8956,9 +9002,24 @@
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
   vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_sse2;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
@@ -9280,6 +9341,8 @@
     vpx_quantize_b = vpx_quantize_b_sse2;
   if (flags & HAS_SSSE3)
     vpx_quantize_b = vpx_quantize_b_ssse3;
+  if (flags & HAS_AVX)
+    vpx_quantize_b = vpx_quantize_b_avx;
   vpx_sad16x16 = vpx_sad16x16_c;
   if (flags & HAS_SSE2)
     vpx_sad16x16 = vpx_sad16x16_sse2;
diff --git a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
index 5aa8e2e..76174db 100644
--- a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
@@ -3695,13 +3695,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -3717,7 +3739,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -7218,6 +7251,15 @@
   vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse4_1;
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index f27b67d..7b71ebe0 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -1,7 +1,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  6
 #define VERSION_PATCH  1
-#define VERSION_EXTRA  "1074-g6b9c691da"
+#define VERSION_EXTRA  "1117-g30c261b1e"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.6.1-1074-g6b9c691da"
-#define VERSION_STRING      " v1.6.1-1074-g6b9c691da"
+#define VERSION_STRING_NOSP "v1.6.1-1117-g30c261b1e"
+#define VERSION_STRING      " v1.6.1-1117-g30c261b1e"
diff --git a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
index 90eff41..10f0c335 100644
--- a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
@@ -4345,13 +4345,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -4370,7 +4392,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -6089,6 +6122,19 @@
                           uint16_t* eob_ptr,
                           const int16_t* scan,
                           const int16_t* iscan);
+void vpx_quantize_b_avx(const tran_low_t* coeff_ptr,
+                        intptr_t n_coeffs,
+                        int skip_block,
+                        const int16_t* zbin_ptr,
+                        const int16_t* round_ptr,
+                        const int16_t* quant_ptr,
+                        const int16_t* quant_shift_ptr,
+                        tran_low_t* qcoeff_ptr,
+                        tran_low_t* dqcoeff_ptr,
+                        const int16_t* dequant_ptr,
+                        uint16_t* eob_ptr,
+                        const int16_t* scan,
+                        const int16_t* iscan);
 RTCD_EXTERN void (*vpx_quantize_b)(const tran_low_t* coeff_ptr,
                                    intptr_t n_coeffs,
                                    int skip_block,
@@ -8956,9 +9002,24 @@
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
   vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct32x32_1_add = vpx_highbd_idct32x32_1_add_sse2;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_c;
   if (flags & HAS_SSE2)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
@@ -9280,6 +9341,8 @@
     vpx_quantize_b = vpx_quantize_b_sse2;
   if (flags & HAS_SSSE3)
     vpx_quantize_b = vpx_quantize_b_ssse3;
+  if (flags & HAS_AVX)
+    vpx_quantize_b = vpx_quantize_b_avx;
   vpx_sad16x16 = vpx_sad16x16_c;
   if (flags & HAS_SSE2)
     vpx_sad16x16 = vpx_sad16x16_sse2;
diff --git a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
index 5aa8e2e..76174db 100644
--- a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
@@ -3695,13 +3695,35 @@
                                      uint16_t* dest,
                                      int stride,
                                      int bd);
-#define vpx_highbd_idct32x32_1024_add vpx_highbd_idct32x32_1024_add_c
+void vpx_highbd_idct32x32_1024_add_sse2(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+void vpx_highbd_idct32x32_1024_add_sse4_1(const tran_low_t* input,
+                                          uint16_t* dest,
+                                          int stride,
+                                          int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_1024_add)(const tran_low_t* input,
+                                                  uint16_t* dest,
+                                                  int stride,
+                                                  int bd);
 
 void vpx_highbd_idct32x32_135_add_c(const tran_low_t* input,
                                     uint16_t* dest,
                                     int stride,
                                     int bd);
-#define vpx_highbd_idct32x32_135_add vpx_highbd_idct32x32_135_add_c
+void vpx_highbd_idct32x32_135_add_sse2(const tran_low_t* input,
+                                       uint16_t* dest,
+                                       int stride,
+                                       int bd);
+void vpx_highbd_idct32x32_135_add_sse4_1(const tran_low_t* input,
+                                         uint16_t* dest,
+                                         int stride,
+                                         int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_135_add)(const tran_low_t* input,
+                                                 uint16_t* dest,
+                                                 int stride,
+                                                 int bd);
 
 void vpx_highbd_idct32x32_1_add_c(const tran_low_t* input,
                                   uint16_t* dest,
@@ -3717,7 +3739,18 @@
                                    uint16_t* dest,
                                    int stride,
                                    int bd);
-#define vpx_highbd_idct32x32_34_add vpx_highbd_idct32x32_34_add_c
+void vpx_highbd_idct32x32_34_add_sse2(const tran_low_t* input,
+                                      uint16_t* dest,
+                                      int stride,
+                                      int bd);
+void vpx_highbd_idct32x32_34_add_sse4_1(const tran_low_t* input,
+                                        uint16_t* dest,
+                                        int stride,
+                                        int bd);
+RTCD_EXTERN void (*vpx_highbd_idct32x32_34_add)(const tran_low_t* input,
+                                                uint16_t* dest,
+                                                int stride,
+                                                int bd);
 
 void vpx_highbd_idct4x4_16_add_c(const tran_low_t* input,
                                  uint16_t* dest,
@@ -7218,6 +7251,15 @@
   vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct16x16_38_add = vpx_highbd_idct16x16_38_add_sse4_1;
+  vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_1024_add = vpx_highbd_idct32x32_1024_add_sse4_1;
+  vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_135_add = vpx_highbd_idct32x32_135_add_sse4_1;
+  vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse2;
+  if (flags & HAS_SSE4_1)
+    vpx_highbd_idct32x32_34_add = vpx_highbd_idct32x32_34_add_sse4_1;
   vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse2;
   if (flags & HAS_SSE4_1)
     vpx_highbd_idct4x4_16_add = vpx_highbd_idct4x4_16_add_sse4_1;
diff --git a/third_party/snappy/README.chromium b/third_party/snappy/README.chromium
index 9cf4e95..c89b74a 100644
--- a/third_party/snappy/README.chromium
+++ b/third_party/snappy/README.chromium
@@ -1,7 +1,7 @@
 Name: Snappy: A fast compressor/decompressor
 Short Name: snappy
 URL: http://google.github.io/snappy/
-Version: 1.1.6.git.77c12adc192ac6620a0f0d340c99149ec56a97a3
+Version: 1.1.7
 License: New BSD
 License File: src/COPYING
 Security Critical: yes
diff --git a/third_party/snappy/linux/config.h b/third_party/snappy/linux/config.h
index 6c51dba..2595d0e 100644
--- a/third_party/snappy/linux/config.h
+++ b/third_party/snappy/linux/config.h
@@ -55,8 +55,8 @@
 /* Define to 1 if you have the <windows.h> header file. */
 /* #undef HAVE_WINDOWS_H */
 
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel and VAX). */
-/* #undef WORDS_BIGENDIAN */
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef SNAPPY_IS_BIG_ENDIAN */
 
 #endif  // THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_
diff --git a/third_party/snappy/linux/snappy-stubs-public.h b/third_party/snappy/linux/snappy-stubs-public.h
index 394d512..590cae0 100644
--- a/third_party/snappy/linux/snappy-stubs-public.h
+++ b/third_party/snappy/linux/snappy-stubs-public.h
@@ -50,7 +50,7 @@
 
 #define SNAPPY_MAJOR 1
 #define SNAPPY_MINOR 1
-#define SNAPPY_PATCHLEVEL 6
+#define SNAPPY_PATCHLEVEL 7
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
diff --git a/third_party/snappy/mac/config.h b/third_party/snappy/mac/config.h
index bedcfb7a..16bb031 100644
--- a/third_party/snappy/mac/config.h
+++ b/third_party/snappy/mac/config.h
@@ -55,8 +55,8 @@
 /* Define to 1 if you have the <windows.h> header file. */
 /* #undef HAVE_WINDOWS_H */
 
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel and VAX). */
-/* #undef WORDS_BIGENDIAN */
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef SNAPPY_IS_BIG_ENDIAN */
 
 #endif  // THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_
diff --git a/third_party/snappy/mac/snappy-stubs-public.h b/third_party/snappy/mac/snappy-stubs-public.h
index 91c3de97..442d599 100644
--- a/third_party/snappy/mac/snappy-stubs-public.h
+++ b/third_party/snappy/mac/snappy-stubs-public.h
@@ -50,7 +50,7 @@
 
 #define SNAPPY_MAJOR 1
 #define SNAPPY_MINOR 1
-#define SNAPPY_PATCHLEVEL 6
+#define SNAPPY_PATCHLEVEL 7
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
diff --git a/third_party/snappy/win32/config.h b/third_party/snappy/win32/config.h
index 75fcea87..d0db90f0 100644
--- a/third_party/snappy/win32/config.h
+++ b/third_party/snappy/win32/config.h
@@ -55,8 +55,8 @@
 /* Define to 1 if you have the <windows.h> header file. */
 #define HAVE_WINDOWS_H 1
 
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel and VAX). */
-/* #undef WORDS_BIGENDIAN */
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef SNAPPY_IS_BIG_ENDIAN */
 
 #endif  // THIRD_PARTY_SNAPPY_OPENSOURCE_CMAKE_CONFIG_H_
diff --git a/third_party/snappy/win32/snappy-stubs-public.h b/third_party/snappy/win32/snappy-stubs-public.h
index afe3c84..b3fddd3 100644
--- a/third_party/snappy/win32/snappy-stubs-public.h
+++ b/third_party/snappy/win32/snappy-stubs-public.h
@@ -50,7 +50,7 @@
 
 #define SNAPPY_MAJOR 1
 #define SNAPPY_MINOR 1
-#define SNAPPY_PATCHLEVEL 6
+#define SNAPPY_PATCHLEVEL 7
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
diff --git a/third_party/zlib/0003-arm-inffast.patch b/third_party/zlib/0003-arm-inffast.patch
new file mode 100644
index 0000000..9f758c8
--- /dev/null
+++ b/third_party/zlib/0003-arm-inffast.patch
@@ -0,0 +1,2248 @@
+From 1ca9400d89deb0fd8d234cd02c9fd9359dad7a9e Mon Sep 17 00:00:00 2001
+From: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
+Date: Fri, 11 Aug 2017 15:37:44 -0700
+Subject: [PATCH] zlib: inflate using wider loads and stores
+
+In inflate_fast() the output pointer always has plenty of room to write.  This
+means that so long as the target is capable, wide un-aligned loads and stores
+can be used to transfer several bytes at once.
+
+When the reference distance is too short simply unroll the data a little to
+increase the distance. Patch by Simon Rosie.
+
+BUG=697280
+Change-Id: I59854eb25d2b1e43561c8a2afaf9175bf10cf674
+---
+ third_party/zlib/BUILD.gn                |   18 +
+ third_party/zlib/README.chromium         |    1 +
+ third_party/zlib/contrib/arm/chunkcopy.h |  279 ++++++
+ third_party/zlib/contrib/arm/inffast.c   |  307 ++++++
+ third_party/zlib/contrib/arm/inflate.c   | 1571 ++++++++++++++++++++++++++++++
+ 5 files changed, 2176 insertions(+)
+ create mode 100644 third_party/zlib/contrib/arm/chunkcopy.h
+ create mode 100644 third_party/zlib/contrib/arm/inffast.c
+ create mode 100644 third_party/zlib/contrib/arm/inflate.c
+
+diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
+index 4b4db15..8c57ad4 100644
+--- a/third_party/zlib/BUILD.gn
++++ b/third_party/zlib/BUILD.gn
+@@ -2,6 +2,10 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
++if (current_cpu == "arm" || current_cpu == "arm64") {
++  import("//build/config/arm.gni")
++}
++
+ config("zlib_config") {
+   include_dirs = [ "." ]
+ }
+@@ -71,6 +75,20 @@ static_library("zlib") {
+     "zutil.h",
+   ]
+ 
++  if (current_cpu == "arm" || current_cpu == "arm64") {
++    if (arm_use_neon) {
++      sources -= [
++        "inflate.c",
++        "inffast.c",
++      ]
++      sources += [
++        "contrib/arm/inflate.c",
++        "contrib/arm/inffast.c",
++        "contrib/arm/chunkcopy.h",
++      ]
++    }
++  }
++
+   if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
+     sources += [ "x86.c" ]
+   }
+diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
+index 8658580..ff5c6ab 100644
+--- a/third_party/zlib/README.chromium
++++ b/third_party/zlib/README.chromium
+@@ -28,3 +28,4 @@ Local Modifications:
+    https://github.com/jtkukunas/zlib/
+  - 0002-uninitializedcheck.patch: default-initialize state->check, see
+    crbug.com/697481
++ - 0003-arm-inffast.patch: ARM optimized inflate using NEON.
+diff --git a/third_party/zlib/contrib/arm/chunkcopy.h b/third_party/zlib/contrib/arm/chunkcopy.h
+new file mode 100644
+index 0000000..2d6fd6f
+--- /dev/null
++++ b/third_party/zlib/contrib/arm/chunkcopy.h
+@@ -0,0 +1,279 @@
++/* chunkcopy.h -- fast copies and sets
++ * Copyright (C) 2017 ARM, Inc.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#ifndef CHUNKCOPY_H
++#define CHUNKCOPY_H
++
++#include "zutil.h"
++#include <arm_neon.h>
++
++#if __STDC_VERSION__ >= 199901L
++#define Z_RESTRICT restrict
++#else
++#define Z_RESTRICT
++#endif
++
++typedef uint8x16_t chunkcopy_chunk_t;
++#define CHUNKCOPY_CHUNK_SIZE sizeof(chunkcopy_chunk_t)
++
++/*
++   Ask the compiler to perform a wide, unaligned load with an machine
++   instruction appropriate for the chunkcopy_chunk_t type.
++ */
++static inline chunkcopy_chunk_t loadchunk(const unsigned char FAR *s) {
++    chunkcopy_chunk_t c;
++    __builtin_memcpy(&c, s, sizeof(c));
++    return c;
++}
++
++/*
++   Ask the compiler to perform a wide, unaligned store with an machine
++   instruction appropriate for the chunkcopy_chunk_t type.
++ */
++static inline void storechunk(unsigned char FAR *d, chunkcopy_chunk_t c) {
++    __builtin_memcpy(d, &c, sizeof(c));
++}
++
++/*
++   Perform a memcpy-like operation, but assume that length is non-zero and that
++   it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE bytes of output even if
++   the length is shorter than this.
++
++   It also guarantees that it will properly unroll the data if the distance
++   between `out` and `from` is at least CHUNKCOPY_CHUNK_SIZE, which we rely on
++   in chunkcopy_relaxed().
++
++   Aside from better memory bus utilisation, this means that short copies
++   (CHUNKCOPY_CHUNK_SIZE bytes or fewer) will fall straight through the loop
++   without iteration, which will hopefully make the branch prediction more
++   reliable.
++ */
++static inline unsigned char FAR *chunkcopy_core(unsigned char FAR *out,
++                                                const unsigned char FAR *from,
++                                                unsigned len) {
++    int bump = (--len % CHUNKCOPY_CHUNK_SIZE) + 1;
++    storechunk(out, loadchunk(from));
++    out += bump;
++    from += bump;
++    len /= CHUNKCOPY_CHUNK_SIZE;
++    while (len-- > 0) {
++        storechunk(out, loadchunk(from));
++        out += CHUNKCOPY_CHUNK_SIZE;
++        from += CHUNKCOPY_CHUNK_SIZE;
++    }
++    return out;
++}
++
++/*
++   Like chunkcopy_core, but avoid writing beyond of legal output.
++
++   Accepts an additional pointer to the end of safe output.  A generic safe
++   copy would use (out + len), but it's normally the case that the end of the
++   output buffer is beyond the end of the current copy, and this can still be
++   exploited.
++ */
++static inline unsigned char FAR *chunkcopy_core_safe(unsigned char FAR *out,
++                                                     const unsigned char FAR * from,
++                                                     unsigned len,
++                                                     unsigned char FAR *limit) {
++    Assert(out + len <= limit, "chunk copy exceeds safety limit");
++    if (limit - out < CHUNKCOPY_CHUNK_SIZE) {
++        const unsigned char FAR * Z_RESTRICT rfrom = from;
++        if (len & 8) { __builtin_memcpy(out, rfrom, 8); out += 8; rfrom += 8; }
++        if (len & 4) { __builtin_memcpy(out, rfrom, 4); out += 4; rfrom += 4; }
++        if (len & 2) { __builtin_memcpy(out, rfrom, 2); out += 2; rfrom += 2; }
++        if (len & 1) { *out++ = *rfrom++; }
++        return out;
++    }
++    return chunkcopy_core(out, from, len);
++}
++
++/*
++   Perform short copies until distance can be rewritten as being at least
++   CHUNKCOPY_CHUNK_SIZE.
++
++   This assumes that it's OK to overwrite at least the first
++   2*CHUNKCOPY_CHUNK_SIZE bytes of output even if the copy is shorter than
++   this.  This assumption holds within inflate_fast() which starts every
++   iteration with at least 258 bytes of output space available (258 being the
++   maximum length output from a single token; see inffast.c).
++ */
++static inline unsigned char FAR *chunkunroll_relaxed(unsigned char FAR *out,
++                                                     unsigned FAR *dist,
++                                                     unsigned FAR *len) {
++    const unsigned char FAR *from = out - *dist;
++    while (*dist < *len && *dist < CHUNKCOPY_CHUNK_SIZE) {
++        storechunk(out, loadchunk(from));
++        out += *dist;
++        *len -= *dist;
++        *dist += *dist;
++    }
++    return out;
++}
++
++
++static inline uint8x16_t chunkset_vld1q_dup_u8x8(const unsigned char FAR * Z_RESTRICT from) {
++#if defined(__clang__) || defined(__aarch64__)
++    return vreinterpretq_u8_u64(vld1q_dup_u64((void *)from));
++#else
++    /* 32-bit GCC uses an alignment hint for vld1q_dup_u64, even when given a
++     * void pointer, so here's an alternate implementation.
++     */
++    uint8x8_t h = vld1_u8(from);
++    return vcombine_u8(h, h);
++#endif
++}
++
++/*
++   Perform an overlapping copy which behaves as a memset() operation, but
++   supporting periods other than one, and assume that length is non-zero and
++   that it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE*3 bytes of output
++   even if the length is shorter than this.
++ */
++static inline unsigned char FAR *chunkset_core(unsigned char FAR *out,
++                                               unsigned period,
++                                               unsigned len) {
++    uint8x16_t f;
++    int bump = ((len - 1) % sizeof(f)) + 1;
++
++    switch (period) {
++    case 1:
++        f = vld1q_dup_u8(out - 1);
++        vst1q_u8(out, f);
++        out += bump;
++        len -= bump;
++        while (len > 0) {
++            vst1q_u8(out, f);
++            out += sizeof(f);
++            len -= sizeof(f);
++        }
++        return out;
++    case 2:
++        f = vreinterpretq_u8_u16(vld1q_dup_u16((void *)(out - 2)));
++        vst1q_u8(out, f);
++        out += bump;
++        len -= bump;
++        if (len > 0) {
++            f = vreinterpretq_u8_u16(vld1q_dup_u16((void *)(out - 2)));
++            do {
++                vst1q_u8(out, f);
++                out += sizeof(f);
++                len -= sizeof(f);
++            } while (len > 0);
++        }
++        return out;
++    case 4:
++        f = vreinterpretq_u8_u32(vld1q_dup_u32((void *)(out - 4)));
++        vst1q_u8(out, f);
++        out += bump;
++        len -= bump;
++        if (len > 0) {
++            f = vreinterpretq_u8_u32(vld1q_dup_u32((void *)(out - 4)));
++            do {
++                vst1q_u8(out, f);
++                out += sizeof(f);
++                len -= sizeof(f);
++            } while (len > 0);
++        }
++        return out;
++    case 8:
++        f = chunkset_vld1q_dup_u8x8(out - 8);
++        vst1q_u8(out, f);
++        out += bump;
++        len -= bump;
++        if (len > 0) {
++            f = chunkset_vld1q_dup_u8x8(out - 8);
++            do {
++                vst1q_u8(out, f);
++                out += sizeof(f);
++                len -= sizeof(f);
++            } while (len > 0);
++        }
++        return out;
++    }
++    out = chunkunroll_relaxed(out, &period, &len);
++    return chunkcopy_core(out, out - period, len);
++}
++
++/*
++   Perform a memcpy-like operation, but assume that length is non-zero and that
++   it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE bytes of output even if
++   the length is shorter than this.
++
++   Unlike chunkcopy_core() above, no guarantee is made regarding the behaviour
++   of overlapping buffers, regardless of the distance between the pointers.
++   This is reflected in the `restrict`-qualified pointers, allowing the
++   compiler to reorder loads and stores.
++ */
++static inline unsigned char FAR *chunkcopy_relaxed(unsigned char FAR * Z_RESTRICT out,
++                                                   const unsigned char FAR * Z_RESTRICT from,
++                                                   unsigned len) {
++    return chunkcopy_core(out, from, len);
++}
++
++/*
++   Like chunkcopy_relaxed, but avoid writing beyond of legal output.
++
++   Unlike chunkcopy_core_safe() above, no guarantee is made regarding the
++   behaviour of overlapping buffers, regardless of the distance between the
++   pointers.  This is reflected in the `restrict`-qualified pointers, allowing
++   the compiler to reorder loads and stores.
++
++   Accepts an additional pointer to the end of safe output.  A generic safe
++   copy would use (out + len), but it's normally the case that the end of the
++   output buffer is beyond the end of the current copy, and this can still be
++   exploited.
++ */
++static inline unsigned char FAR *chunkcopy_safe(unsigned char FAR *out,
++                                                const unsigned char FAR * Z_RESTRICT from,
++                                                unsigned len,
++                                                unsigned char FAR *limit) {
++    Assert(out + len <= limit, "chunk copy exceeds safety limit");
++    return chunkcopy_core_safe(out, from, len, limit);
++}
++
++/*
++   Perform chunky copy within the same buffer, where the source and destination
++   may potentially overlap.
++
++   Assumes that len > 0 on entry, and that it's safe to write at least
++   CHUNKCOPY_CHUNK_SIZE*3 bytes to the output.
++ */
++static inline unsigned char FAR *chunkcopy_lapped_relaxed(unsigned char FAR *out,
++                                                          unsigned dist,
++                                                          unsigned len) {
++    if (dist < len && dist < CHUNKCOPY_CHUNK_SIZE) {
++        return chunkset_core(out, dist, len);
++    }
++    return chunkcopy_core(out, out - dist, len);
++}
++
++/*
++   Behave like chunkcopy_lapped_relaxed, but avoid writing beyond of legal output.
++
++   Accepts an additional pointer to the end of safe output.  A generic safe
++   copy would use (out + len), but it's normally the case that the end of the
++   output buffer is beyond the end of the current copy, and this can still be
++   exploited.
++ */
++static inline unsigned char FAR *chunkcopy_lapped_safe(unsigned char FAR *out,
++                                                       unsigned dist,
++                                                       unsigned len,
++                                                       unsigned char FAR *limit) {
++    Assert(out + len <= limit, "chunk copy exceeds safety limit");
++    if (limit - out < CHUNKCOPY_CHUNK_SIZE * 3) {
++        /* TODO: try harder to optimise this */
++        while (len-- > 0) {
++            *out = *(out - dist);
++            out++;
++        }
++        return out;
++    }
++    return chunkcopy_lapped_relaxed(out, dist, len);
++}
++
++#undef Z_RESTRICT
++
++#endif /* CHUNKCOPY_H */
+diff --git a/third_party/zlib/contrib/arm/inffast.c b/third_party/zlib/contrib/arm/inffast.c
+new file mode 100644
+index 0000000..f7f5007
+--- /dev/null
++++ b/third_party/zlib/contrib/arm/inffast.c
+@@ -0,0 +1,307 @@
++/* inffast.c -- fast decoding
++ * Copyright (C) 1995-2017 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include "zutil.h"
++#include "inftrees.h"
++#include "inflate.h"
++#include "inffast.h"
++#include "chunkcopy.h"
++
++#ifdef ASMINF
++#  pragma message("Assembler code may have bugs -- use at your own risk")
++#else
++
++/*
++   Decode literal, length, and distance codes and write out the resulting
++   literal and match bytes until either not enough input or output is
++   available, an end-of-block is encountered, or a data error is encountered.
++   When large enough input and output buffers are supplied to inflate(), for
++   example, a 16K input buffer and a 64K output buffer, more than 95% of the
++   inflate execution time is spent in this routine.
++
++   Entry assumptions:
++
++        state->mode == LEN
++        strm->avail_in >= 6
++        strm->avail_out >= 258
++        start >= strm->avail_out
++        state->bits < 8
++
++   On return, state->mode is one of:
++
++        LEN -- ran out of enough output space or enough available input
++        TYPE -- reached end of block code, inflate() to interpret next block
++        BAD -- error in block data
++
++   Notes:
++
++    - The maximum input bits used by a length/distance pair is 15 bits for the
++      length code, 5 bits for the length extra, 15 bits for the distance code,
++      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
++      Therefore if strm->avail_in >= 6, then there is enough input to avoid
++      checking for available input while decoding.
++
++    - The maximum bytes that a single length/distance pair can output is 258
++      bytes, which is the maximum length that can be coded.  inflate_fast()
++      requires strm->avail_out >= 258 for each loop to avoid checking for
++      output space.
++ */
++void ZLIB_INTERNAL inflate_fast(strm, start)
++z_streamp strm;
++unsigned start;         /* inflate()'s starting value for strm->avail_out */
++{
++    struct inflate_state FAR *state;
++    z_const unsigned char FAR *in;      /* local strm->next_in */
++    z_const unsigned char FAR *last;    /* have enough input while in < last */
++    unsigned char FAR *out;     /* local strm->next_out */
++    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
++    unsigned char FAR *end;     /* while out < end, enough space available */
++    unsigned char FAR *limit;   /* safety limit for chunky copies */
++#ifdef INFLATE_STRICT
++    unsigned dmax;              /* maximum distance from zlib header */
++#endif
++    unsigned wsize;             /* window size or zero if not using window */
++    unsigned whave;             /* valid bytes in the window */
++    unsigned wnext;             /* window write index */
++    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
++    unsigned long hold;         /* local strm->hold */
++    unsigned bits;              /* local strm->bits */
++    code const FAR *lcode;      /* local strm->lencode */
++    code const FAR *dcode;      /* local strm->distcode */
++    unsigned lmask;             /* mask for first level of length codes */
++    unsigned dmask;             /* mask for first level of distance codes */
++    code here;                  /* retrieved table entry */
++    unsigned op;                /* code bits, operation, extra bits, or */
++                                /*  window position, window bytes to copy */
++    unsigned len;               /* match length, unused bytes */
++    unsigned dist;              /* match distance */
++    unsigned char FAR *from;    /* where to copy match from */
++
++    /* copy state to local variables */
++    state = (struct inflate_state FAR *)strm->state;
++    in = strm->next_in;
++    last = in + (strm->avail_in - 5);
++    out = strm->next_out;
++    beg = out - (start - strm->avail_out);
++    end = out + (strm->avail_out - 257);
++    limit = out + strm->avail_out;
++#ifdef INFLATE_STRICT
++    dmax = state->dmax;
++#endif
++    wsize = state->wsize;
++    whave = state->whave;
++    wnext = (state->wnext == 0 && whave >= wsize) ? wsize : state->wnext;
++    window = state->window;
++    hold = state->hold;
++    bits = state->bits;
++    lcode = state->lencode;
++    dcode = state->distcode;
++    lmask = (1U << state->lenbits) - 1;
++    dmask = (1U << state->distbits) - 1;
++
++    /* decode literals and length/distances until end-of-block or not enough
++       input data or output space */
++    do {
++        if (bits < 15) {
++            hold += (unsigned long)(*in++) << bits;
++            bits += 8;
++            hold += (unsigned long)(*in++) << bits;
++            bits += 8;
++        }
++        here = lcode[hold & lmask];
++      dolen:
++        op = (unsigned)(here.bits);
++        hold >>= op;
++        bits -= op;
++        op = (unsigned)(here.op);
++        if (op == 0) {                          /* literal */
++            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
++                    "inflate:         literal '%c'\n" :
++                    "inflate:         literal 0x%02x\n", here.val));
++            *out++ = (unsigned char)(here.val);
++        }
++        else if (op & 16) {                     /* length base */
++            len = (unsigned)(here.val);
++            op &= 15;                           /* number of extra bits */
++            if (op) {
++                if (bits < op) {
++                    hold += (unsigned long)(*in++) << bits;
++                    bits += 8;
++                }
++                len += (unsigned)hold & ((1U << op) - 1);
++                hold >>= op;
++                bits -= op;
++            }
++            Tracevv((stderr, "inflate:         length %u\n", len));
++            if (bits < 15) {
++                hold += (unsigned long)(*in++) << bits;
++                bits += 8;
++                hold += (unsigned long)(*in++) << bits;
++                bits += 8;
++            }
++            here = dcode[hold & dmask];
++          dodist:
++            op = (unsigned)(here.bits);
++            hold >>= op;
++            bits -= op;
++            op = (unsigned)(here.op);
++            if (op & 16) {                      /* distance base */
++                dist = (unsigned)(here.val);
++                op &= 15;                       /* number of extra bits */
++                if (bits < op) {
++                    hold += (unsigned long)(*in++) << bits;
++                    bits += 8;
++                    if (bits < op) {
++                        hold += (unsigned long)(*in++) << bits;
++                        bits += 8;
++                    }
++                }
++                dist += (unsigned)hold & ((1U << op) - 1);
++#ifdef INFLATE_STRICT
++                if (dist > dmax) {
++                    strm->msg = (char *)"invalid distance too far back";
++                    state->mode = BAD;
++                    break;
++                }
++#endif
++                hold >>= op;
++                bits -= op;
++                Tracevv((stderr, "inflate:         distance %u\n", dist));
++                op = (unsigned)(out - beg);     /* max distance in output */
++                if (dist > op) {                /* see if copy from window */
++                    op = dist - op;             /* distance back in window */
++                    if (op > whave) {
++                        if (state->sane) {
++                            strm->msg =
++                                (char *)"invalid distance too far back";
++                            state->mode = BAD;
++                            break;
++                        }
++#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
++                        if (len <= op - whave) {
++                            do {
++                                *out++ = 0;
++                            } while (--len);
++                            continue;
++                        }
++                        len -= op - whave;
++                        do {
++                            *out++ = 0;
++                        } while (--op > whave);
++                        if (op == 0) {
++                            from = out - dist;
++                            do {
++                                *out++ = *from++;
++                            } while (--len);
++                            continue;
++                        }
++#endif
++                    }
++                    from = window;
++                    if (wnext >= op) {          /* contiguous in window */
++                        from += wnext - op;
++                    }
++                    else {                      /* wrap around window */
++                        op -= wnext;
++                        from += wsize - op;
++                        if (op < len) {         /* some from end of window */
++                            len -= op;
++                            out = chunkcopy_safe(out, from, op, limit);
++                            from = window;      /* more from start of window */
++                            op = wnext;
++                            /* This (rare) case can create a situation where
++                               the first chunkcopy below must be checked.
++                             */
++                        }
++                    }
++                    if (op < len) {             /* still need some from output */
++                        out = chunkcopy_safe(out, from, op, limit);
++                        len -= op;
++                        /* When dist is small the amount of data that can be
++                           copied from the window is also small, and progress
++                           towards the dangerous end of the output buffer is
++                           also small.  This means that for trivial memsets and
++                           for chunkunroll_relaxed() a safety check is
++                           unnecessary.  However, these conditions may not be
++                           entered at all, and in that case it's possible that
++                           the main copy is near the end.
++                          */
++                        out = chunkunroll_relaxed(out, &dist, &len);
++                        out = chunkcopy_safe(out, out - dist, len, limit);
++                    } else {
++                        /* from points to window, so there is no risk of
++                           overlapping pointers requiring memset-like behaviour
++                         */
++                        out = chunkcopy_safe(out, from, len, limit);
++                    }
++                }
++                else {
++                    /* Whole reference is in range of current output.  No
++                       range checks are necessary because we start with room
++                       for at least 258 bytes of output, so unroll and roundoff
++                       operations can write beyond `out+len` so long as they
++                       stay within 258 bytes of `out`.
++                     */
++                    out = chunkcopy_lapped_relaxed(out, dist, len);
++                }
++            }
++            else if ((op & 64) == 0) {          /* 2nd level distance code */
++                here = dcode[here.val + (hold & ((1U << op) - 1))];
++                goto dodist;
++            }
++            else {
++                strm->msg = (char *)"invalid distance code";
++                state->mode = BAD;
++                break;
++            }
++        }
++        else if ((op & 64) == 0) {              /* 2nd level length code */
++            here = lcode[here.val + (hold & ((1U << op) - 1))];
++            goto dolen;
++        }
++        else if (op & 32) {                     /* end-of-block */
++            Tracevv((stderr, "inflate:         end of block\n"));
++            state->mode = TYPE;
++            break;
++        }
++        else {
++            strm->msg = (char *)"invalid literal/length code";
++            state->mode = BAD;
++            break;
++        }
++    } while (in < last && out < end);
++
++    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
++    len = bits >> 3;
++    in -= len;
++    bits -= len << 3;
++    hold &= (1U << bits) - 1;
++
++    /* update state and return */
++    strm->next_in = in;
++    strm->next_out = out;
++    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
++    strm->avail_out = (unsigned)(out < end ?
++                                 257 + (end - out) : 257 - (out - end));
++    state->hold = hold;
++    state->bits = bits;
++    return;
++}
++
++/*
++   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
++   - Using bit fields for code structure
++   - Different op definition to avoid & for extra bits (do & for table bits)
++   - Three separate decoding do-loops for direct, window, and wnext == 0
++   - Special case for distance > 1 copies to do overlapped load and store copy
++   - Explicit branch predictions (based on measured branch probabilities)
++   - Deferring match copy and interspersed it with decoding subsequent codes
++   - Swapping literal/length else
++   - Swapping window/direct else
++   - Larger unrolled copy loops (three is about right)
++   - Moving len -= 3 statement into middle of loop
++ */
++
++#endif /* !ASMINF */
+diff --git a/third_party/zlib/contrib/arm/inflate.c b/third_party/zlib/contrib/arm/inflate.c
+new file mode 100644
+index 0000000..e40322c
+--- /dev/null
++++ b/third_party/zlib/contrib/arm/inflate.c
+@@ -0,0 +1,1571 @@
++/* inflate.c -- zlib decompression
++ * Copyright (C) 1995-2016 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/*
++ * Change history:
++ *
++ * 1.2.beta0    24 Nov 2002
++ * - First version -- complete rewrite of inflate to simplify code, avoid
++ *   creation of window when not needed, minimize use of window when it is
++ *   needed, make inffast.c even faster, implement gzip decoding, and to
++ *   improve code readability and style over the previous zlib inflate code
++ *
++ * 1.2.beta1    25 Nov 2002
++ * - Use pointers for available input and output checking in inffast.c
++ * - Remove input and output counters in inffast.c
++ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
++ * - Remove unnecessary second byte pull from length extra in inffast.c
++ * - Unroll direct copy to three copies per loop in inffast.c
++ *
++ * 1.2.beta2    4 Dec 2002
++ * - Change external routine names to reduce potential conflicts
++ * - Correct filename to inffixed.h for fixed tables in inflate.c
++ * - Make hbuf[] unsigned char to match parameter type in inflate.c
++ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
++ *   to avoid negation problem on Alphas (64 bit) in inflate.c
++ *
++ * 1.2.beta3    22 Dec 2002
++ * - Add comments on state->bits assertion in inffast.c
++ * - Add comments on op field in inftrees.h
++ * - Fix bug in reuse of allocated window after inflateReset()
++ * - Remove bit fields--back to byte structure for speed
++ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
++ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
++ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
++ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
++ * - Use local copies of stream next and avail values, as well as local bit
++ *   buffer and bit count in inflate()--for speed when inflate_fast() not used
++ *
++ * 1.2.beta4    1 Jan 2003
++ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
++ * - Move a comment on output buffer sizes from inffast.c to inflate.c
++ * - Add comments in inffast.c to introduce the inflate_fast() routine
++ * - Rearrange window copies in inflate_fast() for speed and simplification
++ * - Unroll last copy for window match in inflate_fast()
++ * - Use local copies of window variables in inflate_fast() for speed
++ * - Pull out common wnext == 0 case for speed in inflate_fast()
++ * - Make op and len in inflate_fast() unsigned for consistency
++ * - Add FAR to lcode and dcode declarations in inflate_fast()
++ * - Simplified bad distance check in inflate_fast()
++ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
++ *   source file infback.c to provide a call-back interface to inflate for
++ *   programs like gzip and unzip -- uses window as output buffer to avoid
++ *   window copying
++ *
++ * 1.2.beta5    1 Jan 2003
++ * - Improved inflateBack() interface to allow the caller to provide initial
++ *   input in strm.
++ * - Fixed stored blocks bug in inflateBack()
++ *
++ * 1.2.beta6    4 Jan 2003
++ * - Added comments in inffast.c on effectiveness of POSTINC
++ * - Typecasting all around to reduce compiler warnings
++ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
++ *   make compilers happy
++ * - Changed type of window in inflateBackInit() to unsigned char *
++ *
++ * 1.2.beta7    27 Jan 2003
++ * - Changed many types to unsigned or unsigned short to avoid warnings
++ * - Added inflateCopy() function
++ *
++ * 1.2.0        9 Mar 2003
++ * - Changed inflateBack() interface to provide separate opaque descriptors
++ *   for the in() and out() functions
++ * - Changed inflateBack() argument and in_func typedef to swap the length
++ *   and buffer address return values for the input function
++ * - Check next_in and next_out for Z_NULL on entry to inflate()
++ *
++ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
++ */
++
++#include "zutil.h"
++#include "inftrees.h"
++#include "inflate.h"
++#include "inffast.h"
++#include "contrib/arm/chunkcopy.h"
++
++#ifdef MAKEFIXED
++#  ifndef BUILDFIXED
++#    define BUILDFIXED
++#  endif
++#endif
++
++/* function prototypes */
++local int inflateStateCheck OF((z_streamp strm));
++local void fixedtables OF((struct inflate_state FAR *state));
++local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
++                           unsigned copy));
++#ifdef BUILDFIXED
++   void makefixed OF((void));
++#endif
++local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
++                              unsigned len));
++
++local int inflateStateCheck(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++    if (strm == Z_NULL ||
++        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
++        return 1;
++    state = (struct inflate_state FAR *)strm->state;
++    if (state == Z_NULL || state->strm != strm ||
++        state->mode < HEAD || state->mode > SYNC)
++        return 1;
++    return 0;
++}
++
++int ZEXPORT inflateResetKeep(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    strm->total_in = strm->total_out = state->total = 0;
++    strm->msg = Z_NULL;
++    if (state->wrap)        /* to support ill-conceived Java test suite */
++        strm->adler = state->wrap & 1;
++    state->mode = HEAD;
++    state->last = 0;
++    state->havedict = 0;
++    state->dmax = 32768U;
++    state->head = Z_NULL;
++    state->hold = 0;
++    state->bits = 0;
++    state->lencode = state->distcode = state->next = state->codes;
++    state->sane = 1;
++    state->back = -1;
++    Tracev((stderr, "inflate: reset\n"));
++    return Z_OK;
++}
++
++int ZEXPORT inflateReset(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    state->wsize = 0;
++    state->whave = 0;
++    state->wnext = 0;
++    return inflateResetKeep(strm);
++}
++
++int ZEXPORT inflateReset2(strm, windowBits)
++z_streamp strm;
++int windowBits;
++{
++    int wrap;
++    struct inflate_state FAR *state;
++
++    /* get the state */
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++
++    /* extract wrap request from windowBits parameter */
++    if (windowBits < 0) {
++        wrap = 0;
++        windowBits = -windowBits;
++    }
++    else {
++        wrap = (windowBits >> 4) + 5;
++#ifdef GUNZIP
++        if (windowBits < 48)
++            windowBits &= 15;
++#endif
++    }
++
++    /* set number of window bits, free window if different */
++    if (windowBits && (windowBits < 8 || windowBits > 15))
++        return Z_STREAM_ERROR;
++    if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
++        ZFREE(strm, state->window);
++        state->window = Z_NULL;
++    }
++
++    /* update state and reset the rest of it */
++    state->wrap = wrap;
++    state->wbits = (unsigned)windowBits;
++    return inflateReset(strm);
++}
++
++int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
++z_streamp strm;
++int windowBits;
++const char *version;
++int stream_size;
++{
++    int ret;
++    struct inflate_state FAR *state;
++
++    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
++        stream_size != (int)(sizeof(z_stream)))
++        return Z_VERSION_ERROR;
++    if (strm == Z_NULL) return Z_STREAM_ERROR;
++    strm->msg = Z_NULL;                 /* in case we return an error */
++    if (strm->zalloc == (alloc_func)0) {
++#ifdef Z_SOLO
++        return Z_STREAM_ERROR;
++#else
++        strm->zalloc = zcalloc;
++        strm->opaque = (voidpf)0;
++#endif
++    }
++    if (strm->zfree == (free_func)0)
++#ifdef Z_SOLO
++        return Z_STREAM_ERROR;
++#else
++        strm->zfree = zcfree;
++#endif
++    state = (struct inflate_state FAR *)
++            ZALLOC(strm, 1, sizeof(struct inflate_state));
++    if (state == Z_NULL) return Z_MEM_ERROR;
++    Tracev((stderr, "inflate: allocated\n"));
++    strm->state = (struct internal_state FAR *)state;
++    state->strm = strm;
++    state->window = Z_NULL;
++    state->mode = HEAD;     /* to pass state test in inflateReset2() */
++    ret = inflateReset2(strm, windowBits);
++    if (ret != Z_OK) {
++        ZFREE(strm, state);
++        strm->state = Z_NULL;
++    }
++    return ret;
++}
++
++int ZEXPORT inflateInit_(strm, version, stream_size)
++z_streamp strm;
++const char *version;
++int stream_size;
++{
++    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
++}
++
++int ZEXPORT inflatePrime(strm, bits, value)
++z_streamp strm;
++int bits;
++int value;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if (bits < 0) {
++        state->hold = 0;
++        state->bits = 0;
++        return Z_OK;
++    }
++    if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;
++    value &= (1L << bits) - 1;
++    state->hold += (unsigned)value << state->bits;
++    state->bits += (uInt)bits;
++    return Z_OK;
++}
++
++/*
++   Return state with length and distance decoding tables and index sizes set to
++   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
++   If BUILDFIXED is defined, then instead this routine builds the tables the
++   first time it's called, and returns those tables the first time and
++   thereafter.  This reduces the size of the code by about 2K bytes, in
++   exchange for a little execution time.  However, BUILDFIXED should not be
++   used for threaded applications, since the rewriting of the tables and virgin
++   may not be thread-safe.
++ */
++local void fixedtables(state)
++struct inflate_state FAR *state;
++{
++#ifdef BUILDFIXED
++    static int virgin = 1;
++    static code *lenfix, *distfix;
++    static code fixed[544];
++
++    /* build fixed huffman tables if first call (may not be thread safe) */
++    if (virgin) {
++        unsigned sym, bits;
++        static code *next;
++
++        /* literal/length table */
++        sym = 0;
++        while (sym < 144) state->lens[sym++] = 8;
++        while (sym < 256) state->lens[sym++] = 9;
++        while (sym < 280) state->lens[sym++] = 7;
++        while (sym < 288) state->lens[sym++] = 8;
++        next = fixed;
++        lenfix = next;
++        bits = 9;
++        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
++
++        /* distance table */
++        sym = 0;
++        while (sym < 32) state->lens[sym++] = 5;
++        distfix = next;
++        bits = 5;
++        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
++
++        /* do this just once */
++        virgin = 0;
++    }
++#else /* !BUILDFIXED */
++#   include "inffixed.h"
++#endif /* BUILDFIXED */
++    state->lencode = lenfix;
++    state->lenbits = 9;
++    state->distcode = distfix;
++    state->distbits = 5;
++}
++
++#ifdef MAKEFIXED
++#include <stdio.h>
++
++/*
++   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
++   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
++   those tables to stdout, which would be piped to inffixed.h.  A small program
++   can simply call makefixed to do this:
++
++    void makefixed(void);
++
++    int main(void)
++    {
++        makefixed();
++        return 0;
++    }
++
++   Then that can be linked with zlib built with MAKEFIXED defined and run:
++
++    a.out > inffixed.h
++ */
++void makefixed()
++{
++    unsigned low, size;
++    struct inflate_state state;
++
++    fixedtables(&state);
++    puts("    /* inffixed.h -- table for decoding fixed codes");
++    puts("     * Generated automatically by makefixed().");
++    puts("     */");
++    puts("");
++    puts("    /* WARNING: this file should *not* be used by applications.");
++    puts("       It is part of the implementation of this library and is");
++    puts("       subject to change. Applications should only use zlib.h.");
++    puts("     */");
++    puts("");
++    size = 1U << 9;
++    printf("    static const code lenfix[%u] = {", size);
++    low = 0;
++    for (;;) {
++        if ((low % 7) == 0) printf("\n        ");
++        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
++               state.lencode[low].bits, state.lencode[low].val);
++        if (++low == size) break;
++        putchar(',');
++    }
++    puts("\n    };");
++    size = 1U << 5;
++    printf("\n    static const code distfix[%u] = {", size);
++    low = 0;
++    for (;;) {
++        if ((low % 6) == 0) printf("\n        ");
++        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
++               state.distcode[low].val);
++        if (++low == size) break;
++        putchar(',');
++    }
++    puts("\n    };");
++}
++#endif /* MAKEFIXED */
++
++/*
++   Update the window with the last wsize (normally 32K) bytes written before
++   returning.  If window does not exist yet, create it.  This is only called
++   when a window is already in use, or when output has been written during this
++   inflate call, but the end of the deflate stream has not been reached yet.
++   It is also called to create a window for dictionary data when a dictionary
++   is loaded.
++
++   Providing output buffers larger than 32K to inflate() should provide a speed
++   advantage, since only the last 32K of output is copied to the sliding window
++   upon return from inflate(), and since all distances after the first 32K of
++   output will fall in the output data, making match copies simpler and faster.
++   The advantage may be dependent on the size of the processor's data caches.
++ */
++local int updatewindow(strm, end, copy)
++z_streamp strm;
++const Bytef *end;
++unsigned copy;
++{
++    struct inflate_state FAR *state;
++    unsigned dist;
++
++    state = (struct inflate_state FAR *)strm->state;
++
++    /* if it hasn't been done already, allocate space for the window */
++    if (state->window == Z_NULL) {
++        unsigned wsize = 1U << state->wbits;
++        state->window = (unsigned char FAR *)
++                        ZALLOC(strm, wsize + CHUNKCOPY_CHUNK_SIZE,
++                               sizeof(unsigned char));
++        if (state->window == Z_NULL) return 1;
++#ifdef INFLATE_CLEAR_UNUSED_UNDEFINED
++        /* Copies from the overflow portion of this buffer are undefined and
++           may cause analysis tools to raise a warning if we don't initialize
++           it.  However, this undefined data overwrites other undefined data
++           and is subsequently either overwritten or left deliberately
++           undefined at the end of decode; so there's really no point.
++         */
++        memset(state->window + wsize, 0, CHUNKCOPY_CHUNK_SIZE);
++#endif
++    }
++
++    /* if window not in use yet, initialize */
++    if (state->wsize == 0) {
++        state->wsize = 1U << state->wbits;
++        state->wnext = 0;
++        state->whave = 0;
++    }
++
++    /* copy state->wsize or less output bytes into the circular window */
++    if (copy >= state->wsize) {
++        zmemcpy(state->window, end - state->wsize, state->wsize);
++        state->wnext = 0;
++        state->whave = state->wsize;
++    }
++    else {
++        dist = state->wsize - state->wnext;
++        if (dist > copy) dist = copy;
++        zmemcpy(state->window + state->wnext, end - copy, dist);
++        copy -= dist;
++        if (copy) {
++            zmemcpy(state->window, end - copy, copy);
++            state->wnext = copy;
++            state->whave = state->wsize;
++        }
++        else {
++            state->wnext += dist;
++            if (state->wnext == state->wsize) state->wnext = 0;
++            if (state->whave < state->wsize) state->whave += dist;
++        }
++    }
++    return 0;
++}
++
++/* Macros for inflate(): */
++
++/* check function to use adler32() for zlib or crc32() for gzip */
++#ifdef GUNZIP
++#  define UPDATE(check, buf, len) \
++    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
++#else
++#  define UPDATE(check, buf, len) adler32(check, buf, len)
++#endif
++
++/* check macros for header crc */
++#ifdef GUNZIP
++#  define CRC2(check, word) \
++    do { \
++        hbuf[0] = (unsigned char)(word); \
++        hbuf[1] = (unsigned char)((word) >> 8); \
++        check = crc32(check, hbuf, 2); \
++    } while (0)
++
++#  define CRC4(check, word) \
++    do { \
++        hbuf[0] = (unsigned char)(word); \
++        hbuf[1] = (unsigned char)((word) >> 8); \
++        hbuf[2] = (unsigned char)((word) >> 16); \
++        hbuf[3] = (unsigned char)((word) >> 24); \
++        check = crc32(check, hbuf, 4); \
++    } while (0)
++#endif
++
++/* Load registers with state in inflate() for speed */
++#define LOAD() \
++    do { \
++        put = strm->next_out; \
++        left = strm->avail_out; \
++        next = strm->next_in; \
++        have = strm->avail_in; \
++        hold = state->hold; \
++        bits = state->bits; \
++    } while (0)
++
++/* Restore state from registers in inflate() */
++#define RESTORE() \
++    do { \
++        strm->next_out = put; \
++        strm->avail_out = left; \
++        strm->next_in = next; \
++        strm->avail_in = have; \
++        state->hold = hold; \
++        state->bits = bits; \
++    } while (0)
++
++/* Clear the input bit accumulator */
++#define INITBITS() \
++    do { \
++        hold = 0; \
++        bits = 0; \
++    } while (0)
++
++/* Get a byte of input into the bit accumulator, or return from inflate()
++   if there is no input available. */
++#define PULLBYTE() \
++    do { \
++        if (have == 0) goto inf_leave; \
++        have--; \
++        hold += (unsigned long)(*next++) << bits; \
++        bits += 8; \
++    } while (0)
++
++/* Assure that there are at least n bits in the bit accumulator.  If there is
++   not enough available input to do that, then return from inflate(). */
++#define NEEDBITS(n) \
++    do { \
++        while (bits < (unsigned)(n)) \
++            PULLBYTE(); \
++    } while (0)
++
++/* Return the low n bits of the bit accumulator (n < 16) */
++#define BITS(n) \
++    ((unsigned)hold & ((1U << (n)) - 1))
++
++/* Remove n bits from the bit accumulator */
++#define DROPBITS(n) \
++    do { \
++        hold >>= (n); \
++        bits -= (unsigned)(n); \
++    } while (0)
++
++/* Remove zero to seven bits as needed to go to a byte boundary */
++#define BYTEBITS() \
++    do { \
++        hold >>= bits & 7; \
++        bits -= bits & 7; \
++    } while (0)
++
++/*
++   inflate() uses a state machine to process as much input data and generate as
++   much output data as possible before returning.  The state machine is
++   structured roughly as follows:
++
++    for (;;) switch (state) {
++    ...
++    case STATEn:
++        if (not enough input data or output space to make progress)
++            return;
++        ... make progress ...
++        state = STATEm;
++        break;
++    ...
++    }
++
++   so when inflate() is called again, the same case is attempted again, and
++   if the appropriate resources are provided, the machine proceeds to the
++   next state.  The NEEDBITS() macro is usually the way the state evaluates
++   whether it can proceed or should return.  NEEDBITS() does the return if
++   the requested bits are not available.  The typical use of the BITS macros
++   is:
++
++        NEEDBITS(n);
++        ... do something with BITS(n) ...
++        DROPBITS(n);
++
++   where NEEDBITS(n) either returns from inflate() if there isn't enough
++   input left to load n bits into the accumulator, or it continues.  BITS(n)
++   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
++   the low n bits off the accumulator.  INITBITS() clears the accumulator
++   and sets the number of available bits to zero.  BYTEBITS() discards just
++   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
++   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
++
++   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
++   if there is no input available.  The decoding of variable length codes uses
++   PULLBYTE() directly in order to pull just enough bytes to decode the next
++   code, and no more.
++
++   Some states loop until they get enough input, making sure that enough
++   state information is maintained to continue the loop where it left off
++   if NEEDBITS() returns in the loop.  For example, want, need, and keep
++   would all have to actually be part of the saved state in case NEEDBITS()
++   returns:
++
++    case STATEw:
++        while (want < need) {
++            NEEDBITS(n);
++            keep[want++] = BITS(n);
++            DROPBITS(n);
++        }
++        state = STATEx;
++    case STATEx:
++
++   As shown above, if the next state is also the next case, then the break
++   is omitted.
++
++   A state may also return if there is not enough output space available to
++   complete that state.  Those states are copying stored data, writing a
++   literal byte, and copying a matching string.
++
++   When returning, a "goto inf_leave" is used to update the total counters,
++   update the check value, and determine whether any progress has been made
++   during that inflate() call in order to return the proper return code.
++   Progress is defined as a change in either strm->avail_in or strm->avail_out.
++   When there is a window, goto inf_leave will update the window with the last
++   output written.  If a goto inf_leave occurs in the middle of decompression
++   and there is no window currently, goto inf_leave will create one and copy
++   output to the window for the next call of inflate().
++
++   In this implementation, the flush parameter of inflate() only affects the
++   return code (per zlib.h).  inflate() always writes as much as possible to
++   strm->next_out, given the space available and the provided input--the effect
++   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
++   the allocation of and copying into a sliding window until necessary, which
++   provides the effect documented in zlib.h for Z_FINISH when the entire input
++   stream available.  So the only thing the flush parameter actually does is:
++   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
++   will return Z_BUF_ERROR if it has not reached the end of the stream.
++ */
++
++int ZEXPORT inflate(strm, flush)
++z_streamp strm;
++int flush;
++{
++    struct inflate_state FAR *state;
++    z_const unsigned char FAR *next;    /* next input */
++    unsigned char FAR *put;     /* next output */
++    unsigned have, left;        /* available input and output */
++    unsigned long hold;         /* bit buffer */
++    unsigned bits;              /* bits in bit buffer */
++    unsigned in, out;           /* save starting available input and output */
++    unsigned copy;              /* number of stored or match bytes to copy */
++    unsigned char FAR *from;    /* where to copy match bytes from */
++    code here;                  /* current decoding table entry */
++    code last;                  /* parent table entry */
++    unsigned len;               /* length to copy for repeats, bits to drop */
++    int ret;                    /* return code */
++#ifdef GUNZIP
++    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
++#endif
++    static const unsigned short order[19] = /* permutation of code lengths */
++        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
++
++    if (inflateStateCheck(strm) || strm->next_out == Z_NULL ||
++        (strm->next_in == Z_NULL && strm->avail_in != 0))
++        return Z_STREAM_ERROR;
++
++    state = (struct inflate_state FAR *)strm->state;
++    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
++    LOAD();
++    in = have;
++    out = left;
++    ret = Z_OK;
++    for (;;)
++        switch (state->mode) {
++        case HEAD:
++            if (state->wrap == 0) {
++                state->mode = TYPEDO;
++                break;
++            }
++            NEEDBITS(16);
++#ifdef GUNZIP
++            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
++                if (state->wbits == 0)
++                    state->wbits = 15;
++                state->check = crc32(0L, Z_NULL, 0);
++                CRC2(state->check, hold);
++                INITBITS();
++                state->mode = FLAGS;
++                break;
++            }
++            state->flags = 0;           /* expect zlib header */
++            if (state->head != Z_NULL)
++                state->head->done = -1;
++            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
++#else
++            if (
++#endif
++                ((BITS(8) << 8) + (hold >> 8)) % 31) {
++                strm->msg = (char *)"incorrect header check";
++                state->mode = BAD;
++                break;
++            }
++            if (BITS(4) != Z_DEFLATED) {
++                strm->msg = (char *)"unknown compression method";
++                state->mode = BAD;
++                break;
++            }
++            DROPBITS(4);
++            len = BITS(4) + 8;
++            if (state->wbits == 0)
++                state->wbits = len;
++            if (len > 15 || len > state->wbits) {
++                strm->msg = (char *)"invalid window size";
++                state->mode = BAD;
++                break;
++            }
++            state->dmax = 1U << len;
++            Tracev((stderr, "inflate:   zlib header ok\n"));
++            strm->adler = state->check = adler32(0L, Z_NULL, 0);
++            state->mode = hold & 0x200 ? DICTID : TYPE;
++            INITBITS();
++            break;
++#ifdef GUNZIP
++        case FLAGS:
++            NEEDBITS(16);
++            state->flags = (int)(hold);
++            if ((state->flags & 0xff) != Z_DEFLATED) {
++                strm->msg = (char *)"unknown compression method";
++                state->mode = BAD;
++                break;
++            }
++            if (state->flags & 0xe000) {
++                strm->msg = (char *)"unknown header flags set";
++                state->mode = BAD;
++                break;
++            }
++            if (state->head != Z_NULL)
++                state->head->text = (int)((hold >> 8) & 1);
++            if ((state->flags & 0x0200) && (state->wrap & 4))
++                CRC2(state->check, hold);
++            INITBITS();
++            state->mode = TIME;
++        case TIME:
++            NEEDBITS(32);
++            if (state->head != Z_NULL)
++                state->head->time = hold;
++            if ((state->flags & 0x0200) && (state->wrap & 4))
++                CRC4(state->check, hold);
++            INITBITS();
++            state->mode = OS;
++        case OS:
++            NEEDBITS(16);
++            if (state->head != Z_NULL) {
++                state->head->xflags = (int)(hold & 0xff);
++                state->head->os = (int)(hold >> 8);
++            }
++            if ((state->flags & 0x0200) && (state->wrap & 4))
++                CRC2(state->check, hold);
++            INITBITS();
++            state->mode = EXLEN;
++        case EXLEN:
++            if (state->flags & 0x0400) {
++                NEEDBITS(16);
++                state->length = (unsigned)(hold);
++                if (state->head != Z_NULL)
++                    state->head->extra_len = (unsigned)hold;
++                if ((state->flags & 0x0200) && (state->wrap & 4))
++                    CRC2(state->check, hold);
++                INITBITS();
++            }
++            else if (state->head != Z_NULL)
++                state->head->extra = Z_NULL;
++            state->mode = EXTRA;
++        case EXTRA:
++            if (state->flags & 0x0400) {
++                copy = state->length;
++                if (copy > have) copy = have;
++                if (copy) {
++                    if (state->head != Z_NULL &&
++                        state->head->extra != Z_NULL) {
++                        len = state->head->extra_len - state->length;
++                        zmemcpy(state->head->extra + len, next,
++                                len + copy > state->head->extra_max ?
++                                state->head->extra_max - len : copy);
++                    }
++                    if ((state->flags & 0x0200) && (state->wrap & 4))
++                        state->check = crc32(state->check, next, copy);
++                    have -= copy;
++                    next += copy;
++                    state->length -= copy;
++                }
++                if (state->length) goto inf_leave;
++            }
++            state->length = 0;
++            state->mode = NAME;
++        case NAME:
++            if (state->flags & 0x0800) {
++                if (have == 0) goto inf_leave;
++                copy = 0;
++                do {
++                    len = (unsigned)(next[copy++]);
++                    if (state->head != Z_NULL &&
++                            state->head->name != Z_NULL &&
++                            state->length < state->head->name_max)
++                        state->head->name[state->length++] = (Bytef)len;
++                } while (len && copy < have);
++                if ((state->flags & 0x0200) && (state->wrap & 4))
++                    state->check = crc32(state->check, next, copy);
++                have -= copy;
++                next += copy;
++                if (len) goto inf_leave;
++            }
++            else if (state->head != Z_NULL)
++                state->head->name = Z_NULL;
++            state->length = 0;
++            state->mode = COMMENT;
++        case COMMENT:
++            if (state->flags & 0x1000) {
++                if (have == 0) goto inf_leave;
++                copy = 0;
++                do {
++                    len = (unsigned)(next[copy++]);
++                    if (state->head != Z_NULL &&
++                            state->head->comment != Z_NULL &&
++                            state->length < state->head->comm_max)
++                        state->head->comment[state->length++] = (Bytef)len;
++                } while (len && copy < have);
++                if ((state->flags & 0x0200) && (state->wrap & 4))
++                    state->check = crc32(state->check, next, copy);
++                have -= copy;
++                next += copy;
++                if (len) goto inf_leave;
++            }
++            else if (state->head != Z_NULL)
++                state->head->comment = Z_NULL;
++            state->mode = HCRC;
++        case HCRC:
++            if (state->flags & 0x0200) {
++                NEEDBITS(16);
++                if ((state->wrap & 4) && hold != (state->check & 0xffff)) {
++                    strm->msg = (char *)"header crc mismatch";
++                    state->mode = BAD;
++                    break;
++                }
++                INITBITS();
++            }
++            if (state->head != Z_NULL) {
++                state->head->hcrc = (int)((state->flags >> 9) & 1);
++                state->head->done = 1;
++            }
++            strm->adler = state->check = crc32(0L, Z_NULL, 0);
++            state->mode = TYPE;
++            break;
++#endif
++        case DICTID:
++            NEEDBITS(32);
++            strm->adler = state->check = ZSWAP32(hold);
++            INITBITS();
++            state->mode = DICT;
++        case DICT:
++            if (state->havedict == 0) {
++                RESTORE();
++                return Z_NEED_DICT;
++            }
++            strm->adler = state->check = adler32(0L, Z_NULL, 0);
++            state->mode = TYPE;
++        case TYPE:
++            if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
++        case TYPEDO:
++            if (state->last) {
++                BYTEBITS();
++                state->mode = CHECK;
++                break;
++            }
++            NEEDBITS(3);
++            state->last = BITS(1);
++            DROPBITS(1);
++            switch (BITS(2)) {
++            case 0:                             /* stored block */
++                Tracev((stderr, "inflate:     stored block%s\n",
++                        state->last ? " (last)" : ""));
++                state->mode = STORED;
++                break;
++            case 1:                             /* fixed block */
++                fixedtables(state);
++                Tracev((stderr, "inflate:     fixed codes block%s\n",
++                        state->last ? " (last)" : ""));
++                state->mode = LEN_;             /* decode codes */
++                if (flush == Z_TREES) {
++                    DROPBITS(2);
++                    goto inf_leave;
++                }
++                break;
++            case 2:                             /* dynamic block */
++                Tracev((stderr, "inflate:     dynamic codes block%s\n",
++                        state->last ? " (last)" : ""));
++                state->mode = TABLE;
++                break;
++            case 3:
++                strm->msg = (char *)"invalid block type";
++                state->mode = BAD;
++            }
++            DROPBITS(2);
++            break;
++        case STORED:
++            BYTEBITS();                         /* go to byte boundary */
++            NEEDBITS(32);
++            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
++                strm->msg = (char *)"invalid stored block lengths";
++                state->mode = BAD;
++                break;
++            }
++            state->length = (unsigned)hold & 0xffff;
++            Tracev((stderr, "inflate:       stored length %u\n",
++                    state->length));
++            INITBITS();
++            state->mode = COPY_;
++            if (flush == Z_TREES) goto inf_leave;
++        case COPY_:
++            state->mode = COPY;
++        case COPY:
++            copy = state->length;
++            if (copy) {
++                if (copy > have) copy = have;
++                if (copy > left) copy = left;
++                if (copy == 0) goto inf_leave;
++                zmemcpy(put, next, copy);
++                have -= copy;
++                next += copy;
++                left -= copy;
++                put += copy;
++                state->length -= copy;
++                break;
++            }
++            Tracev((stderr, "inflate:       stored end\n"));
++            state->mode = TYPE;
++            break;
++        case TABLE:
++            NEEDBITS(14);
++            state->nlen = BITS(5) + 257;
++            DROPBITS(5);
++            state->ndist = BITS(5) + 1;
++            DROPBITS(5);
++            state->ncode = BITS(4) + 4;
++            DROPBITS(4);
++#ifndef PKZIP_BUG_WORKAROUND
++            if (state->nlen > 286 || state->ndist > 30) {
++                strm->msg = (char *)"too many length or distance symbols";
++                state->mode = BAD;
++                break;
++            }
++#endif
++            Tracev((stderr, "inflate:       table sizes ok\n"));
++            state->have = 0;
++            state->mode = LENLENS;
++        case LENLENS:
++            while (state->have < state->ncode) {
++                NEEDBITS(3);
++                state->lens[order[state->have++]] = (unsigned short)BITS(3);
++                DROPBITS(3);
++            }
++            while (state->have < 19)
++                state->lens[order[state->have++]] = 0;
++            state->next = state->codes;
++            state->lencode = (const code FAR *)(state->next);
++            state->lenbits = 7;
++            ret = inflate_table(CODES, state->lens, 19, &(state->next),
++                                &(state->lenbits), state->work);
++            if (ret) {
++                strm->msg = (char *)"invalid code lengths set";
++                state->mode = BAD;
++                break;
++            }
++            Tracev((stderr, "inflate:       code lengths ok\n"));
++            state->have = 0;
++            state->mode = CODELENS;
++        case CODELENS:
++            while (state->have < state->nlen + state->ndist) {
++                for (;;) {
++                    here = state->lencode[BITS(state->lenbits)];
++                    if ((unsigned)(here.bits) <= bits) break;
++                    PULLBYTE();
++                }
++                if (here.val < 16) {
++                    DROPBITS(here.bits);
++                    state->lens[state->have++] = here.val;
++                }
++                else {
++                    if (here.val == 16) {
++                        NEEDBITS(here.bits + 2);
++                        DROPBITS(here.bits);
++                        if (state->have == 0) {
++                            strm->msg = (char *)"invalid bit length repeat";
++                            state->mode = BAD;
++                            break;
++                        }
++                        len = state->lens[state->have - 1];
++                        copy = 3 + BITS(2);
++                        DROPBITS(2);
++                    }
++                    else if (here.val == 17) {
++                        NEEDBITS(here.bits + 3);
++                        DROPBITS(here.bits);
++                        len = 0;
++                        copy = 3 + BITS(3);
++                        DROPBITS(3);
++                    }
++                    else {
++                        NEEDBITS(here.bits + 7);
++                        DROPBITS(here.bits);
++                        len = 0;
++                        copy = 11 + BITS(7);
++                        DROPBITS(7);
++                    }
++                    if (state->have + copy > state->nlen + state->ndist) {
++                        strm->msg = (char *)"invalid bit length repeat";
++                        state->mode = BAD;
++                        break;
++                    }
++                    while (copy--)
++                        state->lens[state->have++] = (unsigned short)len;
++                }
++            }
++
++            /* handle error breaks in while */
++            if (state->mode == BAD) break;
++
++            /* check for end-of-block code (better have one) */
++            if (state->lens[256] == 0) {
++                strm->msg = (char *)"invalid code -- missing end-of-block";
++                state->mode = BAD;
++                break;
++            }
++
++            /* build code tables -- note: do not change the lenbits or distbits
++               values here (9 and 6) without reading the comments in inftrees.h
++               concerning the ENOUGH constants, which depend on those values */
++            state->next = state->codes;
++            state->lencode = (const code FAR *)(state->next);
++            state->lenbits = 9;
++            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
++                                &(state->lenbits), state->work);
++            if (ret) {
++                strm->msg = (char *)"invalid literal/lengths set";
++                state->mode = BAD;
++                break;
++            }
++            state->distcode = (const code FAR *)(state->next);
++            state->distbits = 6;
++            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
++                            &(state->next), &(state->distbits), state->work);
++            if (ret) {
++                strm->msg = (char *)"invalid distances set";
++                state->mode = BAD;
++                break;
++            }
++            Tracev((stderr, "inflate:       codes ok\n"));
++            state->mode = LEN_;
++            if (flush == Z_TREES) goto inf_leave;
++        case LEN_:
++            state->mode = LEN;
++        case LEN:
++            if (have >= 6 && left >= 258) {
++                RESTORE();
++                inflate_fast(strm, out);
++                LOAD();
++                if (state->mode == TYPE)
++                    state->back = -1;
++                break;
++            }
++            state->back = 0;
++            for (;;) {
++                here = state->lencode[BITS(state->lenbits)];
++                if ((unsigned)(here.bits) <= bits) break;
++                PULLBYTE();
++            }
++            if (here.op && (here.op & 0xf0) == 0) {
++                last = here;
++                for (;;) {
++                    here = state->lencode[last.val +
++                            (BITS(last.bits + last.op) >> last.bits)];
++                    if ((unsigned)(last.bits + here.bits) <= bits) break;
++                    PULLBYTE();
++                }
++                DROPBITS(last.bits);
++                state->back += last.bits;
++            }
++            DROPBITS(here.bits);
++            state->back += here.bits;
++            state->length = (unsigned)here.val;
++            if ((int)(here.op) == 0) {
++                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
++                        "inflate:         literal '%c'\n" :
++                        "inflate:         literal 0x%02x\n", here.val));
++                state->mode = LIT;
++                break;
++            }
++            if (here.op & 32) {
++                Tracevv((stderr, "inflate:         end of block\n"));
++                state->back = -1;
++                state->mode = TYPE;
++                break;
++            }
++            if (here.op & 64) {
++                strm->msg = (char *)"invalid literal/length code";
++                state->mode = BAD;
++                break;
++            }
++            state->extra = (unsigned)(here.op) & 15;
++            state->mode = LENEXT;
++        case LENEXT:
++            if (state->extra) {
++                NEEDBITS(state->extra);
++                state->length += BITS(state->extra);
++                DROPBITS(state->extra);
++                state->back += state->extra;
++            }
++            Tracevv((stderr, "inflate:         length %u\n", state->length));
++            state->was = state->length;
++            state->mode = DIST;
++        case DIST:
++            for (;;) {
++                here = state->distcode[BITS(state->distbits)];
++                if ((unsigned)(here.bits) <= bits) break;
++                PULLBYTE();
++            }
++            if ((here.op & 0xf0) == 0) {
++                last = here;
++                for (;;) {
++                    here = state->distcode[last.val +
++                            (BITS(last.bits + last.op) >> last.bits)];
++                    if ((unsigned)(last.bits + here.bits) <= bits) break;
++                    PULLBYTE();
++                }
++                DROPBITS(last.bits);
++                state->back += last.bits;
++            }
++            DROPBITS(here.bits);
++            state->back += here.bits;
++            if (here.op & 64) {
++                strm->msg = (char *)"invalid distance code";
++                state->mode = BAD;
++                break;
++            }
++            state->offset = (unsigned)here.val;
++            state->extra = (unsigned)(here.op) & 15;
++            state->mode = DISTEXT;
++        case DISTEXT:
++            if (state->extra) {
++                NEEDBITS(state->extra);
++                state->offset += BITS(state->extra);
++                DROPBITS(state->extra);
++                state->back += state->extra;
++            }
++#ifdef INFLATE_STRICT
++            if (state->offset > state->dmax) {
++                strm->msg = (char *)"invalid distance too far back";
++                state->mode = BAD;
++                break;
++            }
++#endif
++            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
++            state->mode = MATCH;
++        case MATCH:
++            if (left == 0) goto inf_leave;
++            copy = out - left;
++            if (state->offset > copy) {         /* copy from window */
++                copy = state->offset - copy;
++                if (copy > state->whave) {
++                    if (state->sane) {
++                        strm->msg = (char *)"invalid distance too far back";
++                        state->mode = BAD;
++                        break;
++                    }
++#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
++                    Trace((stderr, "inflate.c too far\n"));
++                    copy -= state->whave;
++                    if (copy > state->length) copy = state->length;
++                    if (copy > left) copy = left;
++                    left -= copy;
++                    state->length -= copy;
++                    do {
++                        *put++ = 0;
++                    } while (--copy);
++                    if (state->length == 0) state->mode = LEN;
++                    break;
++#endif
++                }
++                if (copy > state->wnext) {
++                    copy -= state->wnext;
++                    from = state->window + (state->wsize - copy);
++                }
++                else
++                    from = state->window + (state->wnext - copy);
++                if (copy > state->length) copy = state->length;
++                if (copy > left) copy = left;
++                put = chunkcopy_safe(put, from, copy, put + left);
++            }
++            else {                              /* copy from output */
++                copy = state->length;
++                if (copy > left) copy = left;
++                put = chunkcopy_lapped_safe(put, state->offset, copy, put + left);
++            }
++            left -= copy;
++            state->length -= copy;
++            if (state->length == 0) state->mode = LEN;
++            break;
++        case LIT:
++            if (left == 0) goto inf_leave;
++            *put++ = (unsigned char)(state->length);
++            left--;
++            state->mode = LEN;
++            break;
++        case CHECK:
++            if (state->wrap) {
++                NEEDBITS(32);
++                out -= left;
++                strm->total_out += out;
++                state->total += out;
++                if ((state->wrap & 4) && out)
++                    strm->adler = state->check =
++                        UPDATE(state->check, put - out, out);
++                out = left;
++                if ((state->wrap & 4) && (
++#ifdef GUNZIP
++                     state->flags ? hold :
++#endif
++                     ZSWAP32(hold)) != state->check) {
++                    strm->msg = (char *)"incorrect data check";
++                    state->mode = BAD;
++                    break;
++                }
++                INITBITS();
++                Tracev((stderr, "inflate:   check matches trailer\n"));
++            }
++#ifdef GUNZIP
++            state->mode = LENGTH;
++        case LENGTH:
++            if (state->wrap && state->flags) {
++                NEEDBITS(32);
++                if (hold != (state->total & 0xffffffffUL)) {
++                    strm->msg = (char *)"incorrect length check";
++                    state->mode = BAD;
++                    break;
++                }
++                INITBITS();
++                Tracev((stderr, "inflate:   length matches trailer\n"));
++            }
++#endif
++            state->mode = DONE;
++        case DONE:
++            ret = Z_STREAM_END;
++            goto inf_leave;
++        case BAD:
++            ret = Z_DATA_ERROR;
++            goto inf_leave;
++        case MEM:
++            return Z_MEM_ERROR;
++        case SYNC:
++        default:
++            return Z_STREAM_ERROR;
++        }
++
++    /*
++       Return from inflate(), updating the total counts and the check value.
++       If there was no progress during the inflate() call, return a buffer
++       error.  Call updatewindow() to create and/or update the window state.
++       Note: a memory error from inflate() is non-recoverable.
++     */
++  inf_leave:
++    RESTORE();
++    if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
++            (state->mode < CHECK || flush != Z_FINISH)))
++        if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
++            state->mode = MEM;
++            return Z_MEM_ERROR;
++        }
++    in -= strm->avail_in;
++    out -= strm->avail_out;
++    strm->total_in += in;
++    strm->total_out += out;
++    state->total += out;
++    if ((state->wrap & 4) && out)
++        strm->adler = state->check =
++            UPDATE(state->check, strm->next_out - out, out);
++    strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
++                      (state->mode == TYPE ? 128 : 0) +
++                      (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
++    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
++        ret = Z_BUF_ERROR;
++    return ret;
++}
++
++int ZEXPORT inflateEnd(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++    if (inflateStateCheck(strm))
++        return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if (state->window != Z_NULL) ZFREE(strm, state->window);
++    ZFREE(strm, strm->state);
++    strm->state = Z_NULL;
++    Tracev((stderr, "inflate: end\n"));
++    return Z_OK;
++}
++
++int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
++z_streamp strm;
++Bytef *dictionary;
++uInt *dictLength;
++{
++    struct inflate_state FAR *state;
++
++    /* check state */
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++
++    /* copy dictionary */
++    if (state->whave && dictionary != Z_NULL) {
++        zmemcpy(dictionary, state->window + state->wnext,
++                state->whave - state->wnext);
++        zmemcpy(dictionary + state->whave - state->wnext,
++                state->window, state->wnext);
++    }
++    if (dictLength != Z_NULL)
++        *dictLength = state->whave;
++    return Z_OK;
++}
++
++int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
++z_streamp strm;
++const Bytef *dictionary;
++uInt dictLength;
++{
++    struct inflate_state FAR *state;
++    unsigned long dictid;
++    int ret;
++
++    /* check state */
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if (state->wrap != 0 && state->mode != DICT)
++        return Z_STREAM_ERROR;
++
++    /* check for correct dictionary identifier */
++    if (state->mode == DICT) {
++        dictid = adler32(0L, Z_NULL, 0);
++        dictid = adler32(dictid, dictionary, dictLength);
++        if (dictid != state->check)
++            return Z_DATA_ERROR;
++    }
++
++    /* copy dictionary to window using updatewindow(), which will amend the
++       existing dictionary if appropriate */
++    ret = updatewindow(strm, dictionary + dictLength, dictLength);
++    if (ret) {
++        state->mode = MEM;
++        return Z_MEM_ERROR;
++    }
++    state->havedict = 1;
++    Tracev((stderr, "inflate:   dictionary set\n"));
++    return Z_OK;
++}
++
++int ZEXPORT inflateGetHeader(strm, head)
++z_streamp strm;
++gz_headerp head;
++{
++    struct inflate_state FAR *state;
++
++    /* check state */
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
++
++    /* save header structure */
++    state->head = head;
++    head->done = 0;
++    return Z_OK;
++}
++
++/*
++   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
++   or when out of input.  When called, *have is the number of pattern bytes
++   found in order so far, in 0..3.  On return *have is updated to the new
++   state.  If on return *have equals four, then the pattern was found and the
++   return value is how many bytes were read including the last byte of the
++   pattern.  If *have is less than four, then the pattern has not been found
++   yet and the return value is len.  In the latter case, syncsearch() can be
++   called again with more data and the *have state.  *have is initialized to
++   zero for the first call.
++ */
++local unsigned syncsearch(have, buf, len)
++unsigned FAR *have;
++const unsigned char FAR *buf;
++unsigned len;
++{
++    unsigned got;
++    unsigned next;
++
++    got = *have;
++    next = 0;
++    while (next < len && got < 4) {
++        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
++            got++;
++        else if (buf[next])
++            got = 0;
++        else
++            got = 4 - got;
++        next++;
++    }
++    *have = got;
++    return next;
++}
++
++int ZEXPORT inflateSync(strm)
++z_streamp strm;
++{
++    unsigned len;               /* number of bytes to look at or looked at */
++    unsigned long in, out;      /* temporary to save total_in and total_out */
++    unsigned char buf[4];       /* to restore bit buffer to byte string */
++    struct inflate_state FAR *state;
++
++    /* check parameters */
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
++
++    /* if first time, start search in bit buffer */
++    if (state->mode != SYNC) {
++        state->mode = SYNC;
++        state->hold <<= state->bits & 7;
++        state->bits -= state->bits & 7;
++        len = 0;
++        while (state->bits >= 8) {
++            buf[len++] = (unsigned char)(state->hold);
++            state->hold >>= 8;
++            state->bits -= 8;
++        }
++        state->have = 0;
++        syncsearch(&(state->have), buf, len);
++    }
++
++    /* search available input */
++    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
++    strm->avail_in -= len;
++    strm->next_in += len;
++    strm->total_in += len;
++
++    /* return no joy or set up to restart inflate() on a new block */
++    if (state->have != 4) return Z_DATA_ERROR;
++    in = strm->total_in;  out = strm->total_out;
++    inflateReset(strm);
++    strm->total_in = in;  strm->total_out = out;
++    state->mode = TYPE;
++    return Z_OK;
++}
++
++/*
++   Returns true if inflate is currently at the end of a block generated by
++   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
++   implementation to provide an additional safety check. PPP uses
++   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
++   block. When decompressing, PPP checks that at the end of input packet,
++   inflate is waiting for these length bytes.
++ */
++int ZEXPORT inflateSyncPoint(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    return state->mode == STORED && state->bits == 0;
++}
++
++int ZEXPORT inflateCopy(dest, source)
++z_streamp dest;
++z_streamp source;
++{
++    struct inflate_state FAR *state;
++    struct inflate_state FAR *copy;
++    unsigned char FAR *window;
++    unsigned wsize;
++
++    /* check input */
++    if (inflateStateCheck(source) || dest == Z_NULL)
++        return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)source->state;
++
++    /* allocate space */
++    copy = (struct inflate_state FAR *)
++           ZALLOC(source, 1, sizeof(struct inflate_state));
++    if (copy == Z_NULL) return Z_MEM_ERROR;
++    window = Z_NULL;
++    if (state->window != Z_NULL) {
++        window = (unsigned char FAR *)
++                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
++        if (window == Z_NULL) {
++            ZFREE(source, copy);
++            return Z_MEM_ERROR;
++        }
++    }
++
++    /* copy state */
++    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
++    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
++    copy->strm = dest;
++    if (state->lencode >= state->codes &&
++        state->lencode <= state->codes + ENOUGH - 1) {
++        copy->lencode = copy->codes + (state->lencode - state->codes);
++        copy->distcode = copy->codes + (state->distcode - state->codes);
++    }
++    copy->next = copy->codes + (state->next - state->codes);
++    if (window != Z_NULL) {
++        wsize = 1U << state->wbits;
++        zmemcpy(window, state->window, wsize);
++    }
++    copy->window = window;
++    dest->state = (struct internal_state FAR *)copy;
++    return Z_OK;
++}
++
++int ZEXPORT inflateUndermine(strm, subvert)
++z_streamp strm;
++int subvert;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
++    state->sane = !subvert;
++    return Z_OK;
++#else
++    (void)subvert;
++    state->sane = 1;
++    return Z_DATA_ERROR;
++#endif
++}
++
++int ZEXPORT inflateValidate(strm, check)
++z_streamp strm;
++int check;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
++    state = (struct inflate_state FAR *)strm->state;
++    if (check)
++        state->wrap |= 4;
++    else
++        state->wrap &= ~4;
++    return Z_OK;
++}
++
++long ZEXPORT inflateMark(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++
++    if (inflateStateCheck(strm))
++        return -(1L << 16);
++    state = (struct inflate_state FAR *)strm->state;
++    return (long)(((unsigned long)((long)state->back)) << 16) +
++        (state->mode == COPY ? state->length :
++            (state->mode == MATCH ? state->was - state->length : 0));
++}
++
++unsigned long ZEXPORT inflateCodesUsed(strm)
++z_streamp strm;
++{
++    struct inflate_state FAR *state;
++    if (inflateStateCheck(strm)) return (unsigned long)-1;
++    state = (struct inflate_state FAR *)strm->state;
++    return (unsigned long)(state->next - state->codes);
++}
+-- 
+2.7.4
+
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 4b4db15b..8c57ad49 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -2,6 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+if (current_cpu == "arm" || current_cpu == "arm64") {
+  import("//build/config/arm.gni")
+}
+
 config("zlib_config") {
   include_dirs = [ "." ]
 }
@@ -71,6 +75,20 @@
     "zutil.h",
   ]
 
+  if (current_cpu == "arm" || current_cpu == "arm64") {
+    if (arm_use_neon) {
+      sources -= [
+        "inflate.c",
+        "inffast.c",
+      ]
+      sources += [
+        "contrib/arm/inflate.c",
+        "contrib/arm/inffast.c",
+        "contrib/arm/chunkcopy.h",
+      ]
+    }
+  }
+
   if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources += [ "x86.c" ]
   }
diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
index 8658580..ff5c6ab 100644
--- a/third_party/zlib/README.chromium
+++ b/third_party/zlib/README.chromium
@@ -28,3 +28,4 @@
    https://github.com/jtkukunas/zlib/
  - 0002-uninitializedcheck.patch: default-initialize state->check, see
    crbug.com/697481
+ - 0003-arm-inffast.patch: ARM optimized inflate using NEON.
diff --git a/third_party/zlib/contrib/arm/chunkcopy.h b/third_party/zlib/contrib/arm/chunkcopy.h
new file mode 100644
index 0000000..2d6fd6f
--- /dev/null
+++ b/third_party/zlib/contrib/arm/chunkcopy.h
@@ -0,0 +1,279 @@
+/* chunkcopy.h -- fast copies and sets
+ * Copyright (C) 2017 ARM, Inc.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#ifndef CHUNKCOPY_H
+#define CHUNKCOPY_H
+
+#include "zutil.h"
+#include <arm_neon.h>
+
+#if __STDC_VERSION__ >= 199901L
+#define Z_RESTRICT restrict
+#else
+#define Z_RESTRICT
+#endif
+
+typedef uint8x16_t chunkcopy_chunk_t;
+#define CHUNKCOPY_CHUNK_SIZE sizeof(chunkcopy_chunk_t)
+
+/*
+   Ask the compiler to perform a wide, unaligned load with an machine
+   instruction appropriate for the chunkcopy_chunk_t type.
+ */
+static inline chunkcopy_chunk_t loadchunk(const unsigned char FAR *s) {
+    chunkcopy_chunk_t c;
+    __builtin_memcpy(&c, s, sizeof(c));
+    return c;
+}
+
+/*
+   Ask the compiler to perform a wide, unaligned store with an machine
+   instruction appropriate for the chunkcopy_chunk_t type.
+ */
+static inline void storechunk(unsigned char FAR *d, chunkcopy_chunk_t c) {
+    __builtin_memcpy(d, &c, sizeof(c));
+}
+
+/*
+   Perform a memcpy-like operation, but assume that length is non-zero and that
+   it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE bytes of output even if
+   the length is shorter than this.
+
+   It also guarantees that it will properly unroll the data if the distance
+   between `out` and `from` is at least CHUNKCOPY_CHUNK_SIZE, which we rely on
+   in chunkcopy_relaxed().
+
+   Aside from better memory bus utilisation, this means that short copies
+   (CHUNKCOPY_CHUNK_SIZE bytes or fewer) will fall straight through the loop
+   without iteration, which will hopefully make the branch prediction more
+   reliable.
+ */
+static inline unsigned char FAR *chunkcopy_core(unsigned char FAR *out,
+                                                const unsigned char FAR *from,
+                                                unsigned len) {
+    int bump = (--len % CHUNKCOPY_CHUNK_SIZE) + 1;
+    storechunk(out, loadchunk(from));
+    out += bump;
+    from += bump;
+    len /= CHUNKCOPY_CHUNK_SIZE;
+    while (len-- > 0) {
+        storechunk(out, loadchunk(from));
+        out += CHUNKCOPY_CHUNK_SIZE;
+        from += CHUNKCOPY_CHUNK_SIZE;
+    }
+    return out;
+}
+
+/*
+   Like chunkcopy_core, but avoid writing beyond of legal output.
+
+   Accepts an additional pointer to the end of safe output.  A generic safe
+   copy would use (out + len), but it's normally the case that the end of the
+   output buffer is beyond the end of the current copy, and this can still be
+   exploited.
+ */
+static inline unsigned char FAR *chunkcopy_core_safe(unsigned char FAR *out,
+                                                     const unsigned char FAR * from,
+                                                     unsigned len,
+                                                     unsigned char FAR *limit) {
+    Assert(out + len <= limit, "chunk copy exceeds safety limit");
+    if (limit - out < CHUNKCOPY_CHUNK_SIZE) {
+        const unsigned char FAR * Z_RESTRICT rfrom = from;
+        if (len & 8) { __builtin_memcpy(out, rfrom, 8); out += 8; rfrom += 8; }
+        if (len & 4) { __builtin_memcpy(out, rfrom, 4); out += 4; rfrom += 4; }
+        if (len & 2) { __builtin_memcpy(out, rfrom, 2); out += 2; rfrom += 2; }
+        if (len & 1) { *out++ = *rfrom++; }
+        return out;
+    }
+    return chunkcopy_core(out, from, len);
+}
+
+/*
+   Perform short copies until distance can be rewritten as being at least
+   CHUNKCOPY_CHUNK_SIZE.
+
+   This assumes that it's OK to overwrite at least the first
+   2*CHUNKCOPY_CHUNK_SIZE bytes of output even if the copy is shorter than
+   this.  This assumption holds within inflate_fast() which starts every
+   iteration with at least 258 bytes of output space available (258 being the
+   maximum length output from a single token; see inffast.c).
+ */
+static inline unsigned char FAR *chunkunroll_relaxed(unsigned char FAR *out,
+                                                     unsigned FAR *dist,
+                                                     unsigned FAR *len) {
+    const unsigned char FAR *from = out - *dist;
+    while (*dist < *len && *dist < CHUNKCOPY_CHUNK_SIZE) {
+        storechunk(out, loadchunk(from));
+        out += *dist;
+        *len -= *dist;
+        *dist += *dist;
+    }
+    return out;
+}
+
+
+static inline uint8x16_t chunkset_vld1q_dup_u8x8(const unsigned char FAR * Z_RESTRICT from) {
+#if defined(__clang__) || defined(__aarch64__)
+    return vreinterpretq_u8_u64(vld1q_dup_u64((void *)from));
+#else
+    /* 32-bit GCC uses an alignment hint for vld1q_dup_u64, even when given a
+     * void pointer, so here's an alternate implementation.
+     */
+    uint8x8_t h = vld1_u8(from);
+    return vcombine_u8(h, h);
+#endif
+}
+
+/*
+   Perform an overlapping copy which behaves as a memset() operation, but
+   supporting periods other than one, and assume that length is non-zero and
+   that it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE*3 bytes of output
+   even if the length is shorter than this.
+ */
+static inline unsigned char FAR *chunkset_core(unsigned char FAR *out,
+                                               unsigned period,
+                                               unsigned len) {
+    uint8x16_t f;
+    int bump = ((len - 1) % sizeof(f)) + 1;
+
+    switch (period) {
+    case 1:
+        f = vld1q_dup_u8(out - 1);
+        vst1q_u8(out, f);
+        out += bump;
+        len -= bump;
+        while (len > 0) {
+            vst1q_u8(out, f);
+            out += sizeof(f);
+            len -= sizeof(f);
+        }
+        return out;
+    case 2:
+        f = vreinterpretq_u8_u16(vld1q_dup_u16((void *)(out - 2)));
+        vst1q_u8(out, f);
+        out += bump;
+        len -= bump;
+        if (len > 0) {
+            f = vreinterpretq_u8_u16(vld1q_dup_u16((void *)(out - 2)));
+            do {
+                vst1q_u8(out, f);
+                out += sizeof(f);
+                len -= sizeof(f);
+            } while (len > 0);
+        }
+        return out;
+    case 4:
+        f = vreinterpretq_u8_u32(vld1q_dup_u32((void *)(out - 4)));
+        vst1q_u8(out, f);
+        out += bump;
+        len -= bump;
+        if (len > 0) {
+            f = vreinterpretq_u8_u32(vld1q_dup_u32((void *)(out - 4)));
+            do {
+                vst1q_u8(out, f);
+                out += sizeof(f);
+                len -= sizeof(f);
+            } while (len > 0);
+        }
+        return out;
+    case 8:
+        f = chunkset_vld1q_dup_u8x8(out - 8);
+        vst1q_u8(out, f);
+        out += bump;
+        len -= bump;
+        if (len > 0) {
+            f = chunkset_vld1q_dup_u8x8(out - 8);
+            do {
+                vst1q_u8(out, f);
+                out += sizeof(f);
+                len -= sizeof(f);
+            } while (len > 0);
+        }
+        return out;
+    }
+    out = chunkunroll_relaxed(out, &period, &len);
+    return chunkcopy_core(out, out - period, len);
+}
+
+/*
+   Perform a memcpy-like operation, but assume that length is non-zero and that
+   it's OK to overwrite at least CHUNKCOPY_CHUNK_SIZE bytes of output even if
+   the length is shorter than this.
+
+   Unlike chunkcopy_core() above, no guarantee is made regarding the behaviour
+   of overlapping buffers, regardless of the distance between the pointers.
+   This is reflected in the `restrict`-qualified pointers, allowing the
+   compiler to reorder loads and stores.
+ */
+static inline unsigned char FAR *chunkcopy_relaxed(unsigned char FAR * Z_RESTRICT out,
+                                                   const unsigned char FAR * Z_RESTRICT from,
+                                                   unsigned len) {
+    return chunkcopy_core(out, from, len);
+}
+
+/*
+   Like chunkcopy_relaxed, but avoid writing beyond of legal output.
+
+   Unlike chunkcopy_core_safe() above, no guarantee is made regarding the
+   behaviour of overlapping buffers, regardless of the distance between the
+   pointers.  This is reflected in the `restrict`-qualified pointers, allowing
+   the compiler to reorder loads and stores.
+
+   Accepts an additional pointer to the end of safe output.  A generic safe
+   copy would use (out + len), but it's normally the case that the end of the
+   output buffer is beyond the end of the current copy, and this can still be
+   exploited.
+ */
+static inline unsigned char FAR *chunkcopy_safe(unsigned char FAR *out,
+                                                const unsigned char FAR * Z_RESTRICT from,
+                                                unsigned len,
+                                                unsigned char FAR *limit) {
+    Assert(out + len <= limit, "chunk copy exceeds safety limit");
+    return chunkcopy_core_safe(out, from, len, limit);
+}
+
+/*
+   Perform chunky copy within the same buffer, where the source and destination
+   may potentially overlap.
+
+   Assumes that len > 0 on entry, and that it's safe to write at least
+   CHUNKCOPY_CHUNK_SIZE*3 bytes to the output.
+ */
+static inline unsigned char FAR *chunkcopy_lapped_relaxed(unsigned char FAR *out,
+                                                          unsigned dist,
+                                                          unsigned len) {
+    if (dist < len && dist < CHUNKCOPY_CHUNK_SIZE) {
+        return chunkset_core(out, dist, len);
+    }
+    return chunkcopy_core(out, out - dist, len);
+}
+
+/*
+   Behave like chunkcopy_lapped_relaxed, but avoid writing beyond of legal output.
+
+   Accepts an additional pointer to the end of safe output.  A generic safe
+   copy would use (out + len), but it's normally the case that the end of the
+   output buffer is beyond the end of the current copy, and this can still be
+   exploited.
+ */
+static inline unsigned char FAR *chunkcopy_lapped_safe(unsigned char FAR *out,
+                                                       unsigned dist,
+                                                       unsigned len,
+                                                       unsigned char FAR *limit) {
+    Assert(out + len <= limit, "chunk copy exceeds safety limit");
+    if (limit - out < CHUNKCOPY_CHUNK_SIZE * 3) {
+        /* TODO: try harder to optimise this */
+        while (len-- > 0) {
+            *out = *(out - dist);
+            out++;
+        }
+        return out;
+    }
+    return chunkcopy_lapped_relaxed(out, dist, len);
+}
+
+#undef Z_RESTRICT
+
+#endif /* CHUNKCOPY_H */
diff --git a/third_party/zlib/contrib/arm/inffast.c b/third_party/zlib/contrib/arm/inffast.c
new file mode 100644
index 0000000..f7f5007
--- /dev/null
+++ b/third_party/zlib/contrib/arm/inffast.c
@@ -0,0 +1,307 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2017 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+#include "chunkcopy.h"
+
+#ifdef ASMINF
+#  pragma message("Assembler code may have bugs -- use at your own risk")
+#else
+
+/*
+   Decode literal, length, and distance codes and write out the resulting
+   literal and match bytes until either not enough input or output is
+   available, an end-of-block is encountered, or a data error is encountered.
+   When large enough input and output buffers are supplied to inflate(), for
+   example, a 16K input buffer and a 64K output buffer, more than 95% of the
+   inflate execution time is spent in this routine.
+
+   Entry assumptions:
+
+        state->mode == LEN
+        strm->avail_in >= 6
+        strm->avail_out >= 258
+        start >= strm->avail_out
+        state->bits < 8
+
+   On return, state->mode is one of:
+
+        LEN -- ran out of enough output space or enough available input
+        TYPE -- reached end of block code, inflate() to interpret next block
+        BAD -- error in block data
+
+   Notes:
+
+    - The maximum input bits used by a length/distance pair is 15 bits for the
+      length code, 5 bits for the length extra, 15 bits for the distance code,
+      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
+      Therefore if strm->avail_in >= 6, then there is enough input to avoid
+      checking for available input while decoding.
+
+    - The maximum bytes that a single length/distance pair can output is 258
+      bytes, which is the maximum length that can be coded.  inflate_fast()
+      requires strm->avail_out >= 258 for each loop to avoid checking for
+      output space.
+ */
+void ZLIB_INTERNAL inflate_fast(strm, start)
+z_streamp strm;
+unsigned start;         /* inflate()'s starting value for strm->avail_out */
+{
+    struct inflate_state FAR *state;
+    z_const unsigned char FAR *in;      /* local strm->next_in */
+    z_const unsigned char FAR *last;    /* have enough input while in < last */
+    unsigned char FAR *out;     /* local strm->next_out */
+    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
+    unsigned char FAR *end;     /* while out < end, enough space available */
+    unsigned char FAR *limit;   /* safety limit for chunky copies */
+#ifdef INFLATE_STRICT
+    unsigned dmax;              /* maximum distance from zlib header */
+#endif
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned wnext;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
+    unsigned long hold;         /* local strm->hold */
+    unsigned bits;              /* local strm->bits */
+    code const FAR *lcode;      /* local strm->lencode */
+    code const FAR *dcode;      /* local strm->distcode */
+    unsigned lmask;             /* mask for first level of length codes */
+    unsigned dmask;             /* mask for first level of distance codes */
+    code here;                  /* retrieved table entry */
+    unsigned op;                /* code bits, operation, extra bits, or */
+                                /*  window position, window bytes to copy */
+    unsigned len;               /* match length, unused bytes */
+    unsigned dist;              /* match distance */
+    unsigned char FAR *from;    /* where to copy match from */
+
+    /* copy state to local variables */
+    state = (struct inflate_state FAR *)strm->state;
+    in = strm->next_in;
+    last = in + (strm->avail_in - 5);
+    out = strm->next_out;
+    beg = out - (start - strm->avail_out);
+    end = out + (strm->avail_out - 257);
+    limit = out + strm->avail_out;
+#ifdef INFLATE_STRICT
+    dmax = state->dmax;
+#endif
+    wsize = state->wsize;
+    whave = state->whave;
+    wnext = (state->wnext == 0 && whave >= wsize) ? wsize : state->wnext;
+    window = state->window;
+    hold = state->hold;
+    bits = state->bits;
+    lcode = state->lencode;
+    dcode = state->distcode;
+    lmask = (1U << state->lenbits) - 1;
+    dmask = (1U << state->distbits) - 1;
+
+    /* decode literals and length/distances until end-of-block or not enough
+       input data or output space */
+    do {
+        if (bits < 15) {
+            hold += (unsigned long)(*in++) << bits;
+            bits += 8;
+            hold += (unsigned long)(*in++) << bits;
+            bits += 8;
+        }
+        here = lcode[hold & lmask];
+      dolen:
+        op = (unsigned)(here.bits);
+        hold >>= op;
+        bits -= op;
+        op = (unsigned)(here.op);
+        if (op == 0) {                          /* literal */
+            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+                    "inflate:         literal '%c'\n" :
+                    "inflate:         literal 0x%02x\n", here.val));
+            *out++ = (unsigned char)(here.val);
+        }
+        else if (op & 16) {                     /* length base */
+            len = (unsigned)(here.val);
+            op &= 15;                           /* number of extra bits */
+            if (op) {
+                if (bits < op) {
+                    hold += (unsigned long)(*in++) << bits;
+                    bits += 8;
+                }
+                len += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", len));
+            if (bits < 15) {
+                hold += (unsigned long)(*in++) << bits;
+                bits += 8;
+                hold += (unsigned long)(*in++) << bits;
+                bits += 8;
+            }
+            here = dcode[hold & dmask];
+          dodist:
+            op = (unsigned)(here.bits);
+            hold >>= op;
+            bits -= op;
+            op = (unsigned)(here.op);
+            if (op & 16) {                      /* distance base */
+                dist = (unsigned)(here.val);
+                op &= 15;                       /* number of extra bits */
+                if (bits < op) {
+                    hold += (unsigned long)(*in++) << bits;
+                    bits += 8;
+                    if (bits < op) {
+                        hold += (unsigned long)(*in++) << bits;
+                        bits += 8;
+                    }
+                }
+                dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+                if (dist > dmax) {
+                    strm->msg = (char *)"invalid distance too far back";
+                    state->mode = BAD;
+                    break;
+                }
+#endif
+                hold >>= op;
+                bits -= op;
+                Tracevv((stderr, "inflate:         distance %u\n", dist));
+                op = (unsigned)(out - beg);     /* max distance in output */
+                if (dist > op) {                /* see if copy from window */
+                    op = dist - op;             /* distance back in window */
+                    if (op > whave) {
+                        if (state->sane) {
+                            strm->msg =
+                                (char *)"invalid distance too far back";
+                            state->mode = BAD;
+                            break;
+                        }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+                        if (len <= op - whave) {
+                            do {
+                                *out++ = 0;
+                            } while (--len);
+                            continue;
+                        }
+                        len -= op - whave;
+                        do {
+                            *out++ = 0;
+                        } while (--op > whave);
+                        if (op == 0) {
+                            from = out - dist;
+                            do {
+                                *out++ = *from++;
+                            } while (--len);
+                            continue;
+                        }
+#endif
+                    }
+                    from = window;
+                    if (wnext >= op) {          /* contiguous in window */
+                        from += wnext - op;
+                    }
+                    else {                      /* wrap around window */
+                        op -= wnext;
+                        from += wsize - op;
+                        if (op < len) {         /* some from end of window */
+                            len -= op;
+                            out = chunkcopy_safe(out, from, op, limit);
+                            from = window;      /* more from start of window */
+                            op = wnext;
+                            /* This (rare) case can create a situation where
+                               the first chunkcopy below must be checked.
+                             */
+                        }
+                    }
+                    if (op < len) {             /* still need some from output */
+                        out = chunkcopy_safe(out, from, op, limit);
+                        len -= op;
+                        /* When dist is small the amount of data that can be
+                           copied from the window is also small, and progress
+                           towards the dangerous end of the output buffer is
+                           also small.  This means that for trivial memsets and
+                           for chunkunroll_relaxed() a safety check is
+                           unnecessary.  However, these conditions may not be
+                           entered at all, and in that case it's possible that
+                           the main copy is near the end.
+                          */
+                        out = chunkunroll_relaxed(out, &dist, &len);
+                        out = chunkcopy_safe(out, out - dist, len, limit);
+                    } else {
+                        /* from points to window, so there is no risk of
+                           overlapping pointers requiring memset-like behaviour
+                         */
+                        out = chunkcopy_safe(out, from, len, limit);
+                    }
+                }
+                else {
+                    /* Whole reference is in range of current output.  No
+                       range checks are necessary because we start with room
+                       for at least 258 bytes of output, so unroll and roundoff
+                       operations can write beyond `out+len` so long as they
+                       stay within 258 bytes of `out`.
+                     */
+                    out = chunkcopy_lapped_relaxed(out, dist, len);
+                }
+            }
+            else if ((op & 64) == 0) {          /* 2nd level distance code */
+                here = dcode[here.val + (hold & ((1U << op) - 1))];
+                goto dodist;
+            }
+            else {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+        }
+        else if ((op & 64) == 0) {              /* 2nd level length code */
+            here = lcode[here.val + (hold & ((1U << op) - 1))];
+            goto dolen;
+        }
+        else if (op & 32) {                     /* end-of-block */
+            Tracevv((stderr, "inflate:         end of block\n"));
+            state->mode = TYPE;
+            break;
+        }
+        else {
+            strm->msg = (char *)"invalid literal/length code";
+            state->mode = BAD;
+            break;
+        }
+    } while (in < last && out < end);
+
+    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+    len = bits >> 3;
+    in -= len;
+    bits -= len << 3;
+    hold &= (1U << bits) - 1;
+
+    /* update state and return */
+    strm->next_in = in;
+    strm->next_out = out;
+    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+    strm->avail_out = (unsigned)(out < end ?
+                                 257 + (end - out) : 257 - (out - end));
+    state->hold = hold;
+    state->bits = bits;
+    return;
+}
+
+/*
+   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+   - Using bit fields for code structure
+   - Different op definition to avoid & for extra bits (do & for table bits)
+   - Three separate decoding do-loops for direct, window, and wnext == 0
+   - Special case for distance > 1 copies to do overlapped load and store copy
+   - Explicit branch predictions (based on measured branch probabilities)
+   - Deferring match copy and interspersed it with decoding subsequent codes
+   - Swapping literal/length else
+   - Swapping window/direct else
+   - Larger unrolled copy loops (three is about right)
+   - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/third_party/zlib/contrib/arm/inflate.c b/third_party/zlib/contrib/arm/inflate.c
new file mode 100644
index 0000000..e40322c
--- /dev/null
+++ b/third_party/zlib/contrib/arm/inflate.c
@@ -0,0 +1,1571 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0    24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ *   creation of window when not needed, minimize use of window when it is
+ *   needed, make inffast.c even faster, implement gzip decoding, and to
+ *   improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1    25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2    4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ *   to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3    22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ *   buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4    1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common wnext == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ *   source file infback.c to provide a call-back interface to inflate for
+ *   programs like gzip and unzip -- uses window as output buffer to avoid
+ *   window copying
+ *
+ * 1.2.beta5    1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ *   input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6    4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ *   make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7    27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0        9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ *   for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ *   and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+#include "contrib/arm/chunkcopy.h"
+
+#ifdef MAKEFIXED
+#  ifndef BUILDFIXED
+#    define BUILDFIXED
+#  endif
+#endif
+
+/* function prototypes */
+local int inflateStateCheck OF((z_streamp strm));
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
+                           unsigned copy));
+#ifdef BUILDFIXED
+   void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
+                              unsigned len));
+
+local int inflateStateCheck(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (strm == Z_NULL ||
+        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
+        return 1;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state == Z_NULL || state->strm != strm ||
+        state->mode < HEAD || state->mode > SYNC)
+        return 1;
+    return 0;
+}
+
+int ZEXPORT inflateResetKeep(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    strm->total_in = strm->total_out = state->total = 0;
+    strm->msg = Z_NULL;
+    if (state->wrap)        /* to support ill-conceived Java test suite */
+        strm->adler = state->wrap & 1;
+    state->mode = HEAD;
+    state->last = 0;
+    state->havedict = 0;
+    state->dmax = 32768U;
+    state->head = Z_NULL;
+    state->hold = 0;
+    state->bits = 0;
+    state->lencode = state->distcode = state->next = state->codes;
+    state->sane = 1;
+    state->back = -1;
+    Tracev((stderr, "inflate: reset\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    state->wsize = 0;
+    state->whave = 0;
+    state->wnext = 0;
+    return inflateResetKeep(strm);
+}
+
+int ZEXPORT inflateReset2(strm, windowBits)
+z_streamp strm;
+int windowBits;
+{
+    int wrap;
+    struct inflate_state FAR *state;
+
+    /* get the state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* extract wrap request from windowBits parameter */
+    if (windowBits < 0) {
+        wrap = 0;
+        windowBits = -windowBits;
+    }
+    else {
+        wrap = (windowBits >> 4) + 5;
+#ifdef GUNZIP
+        if (windowBits < 48)
+            windowBits &= 15;
+#endif
+    }
+
+    /* set number of window bits, free window if different */
+    if (windowBits && (windowBits < 8 || windowBits > 15))
+        return Z_STREAM_ERROR;
+    if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
+        ZFREE(strm, state->window);
+        state->window = Z_NULL;
+    }
+
+    /* update state and reset the rest of it */
+    state->wrap = wrap;
+    state->wbits = (unsigned)windowBits;
+    return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+    int ret;
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+#endif
+    }
+    if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zfree = zcfree;
+#endif
+    state = (struct inflate_state FAR *)
+            ZALLOC(strm, 1, sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (struct internal_state FAR *)state;
+    state->strm = strm;
+    state->window = Z_NULL;
+    state->mode = HEAD;     /* to pass state test in inflateReset2() */
+    ret = inflateReset2(strm, windowBits);
+    if (ret != Z_OK) {
+        ZFREE(strm, state);
+        strm->state = Z_NULL;
+    }
+    return ret;
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (bits < 0) {
+        state->hold = 0;
+        state->bits = 0;
+        return Z_OK;
+    }
+    if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;
+    value &= (1L << bits) - 1;
+    state->hold += (unsigned)value << state->bits;
+    state->bits += (uInt)bits;
+    return Z_OK;
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
+   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
+   those tables to stdout, which would be piped to inffixed.h.  A small program
+   can simply call makefixed to do this:
+
+    void makefixed(void);
+
+    int main(void)
+    {
+        makefixed();
+        return 0;
+    }
+
+   Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+    a.out > inffixed.h
+ */
+void makefixed()
+{
+    unsigned low, size;
+    struct inflate_state state;
+
+    fixedtables(&state);
+    puts("    /* inffixed.h -- table for decoding fixed codes");
+    puts("     * Generated automatically by makefixed().");
+    puts("     */");
+    puts("");
+    puts("    /* WARNING: this file should *not* be used by applications.");
+    puts("       It is part of the implementation of this library and is");
+    puts("       subject to change. Applications should only use zlib.h.");
+    puts("     */");
+    puts("");
+    size = 1U << 9;
+    printf("    static const code lenfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 7) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
+               state.lencode[low].bits, state.lencode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+    size = 1U << 5;
+    printf("\n    static const code distfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 6) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+               state.distcode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+}
+#endif /* MAKEFIXED */
+
+/*
+   Update the window with the last wsize (normally 32K) bytes written before
+   returning.  If window does not exist yet, create it.  This is only called
+   when a window is already in use, or when output has been written during this
+   inflate call, but the end of the deflate stream has not been reached yet.
+   It is also called to create a window for dictionary data when a dictionary
+   is loaded.
+
+   Providing output buffers larger than 32K to inflate() should provide a speed
+   advantage, since only the last 32K of output is copied to the sliding window
+   upon return from inflate(), and since all distances after the first 32K of
+   output will fall in the output data, making match copies simpler and faster.
+   The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, end, copy)
+z_streamp strm;
+const Bytef *end;
+unsigned copy;
+{
+    struct inflate_state FAR *state;
+    unsigned dist;
+
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* if it hasn't been done already, allocate space for the window */
+    if (state->window == Z_NULL) {
+        unsigned wsize = 1U << state->wbits;
+        state->window = (unsigned char FAR *)
+                        ZALLOC(strm, wsize + CHUNKCOPY_CHUNK_SIZE,
+                               sizeof(unsigned char));
+        if (state->window == Z_NULL) return 1;
+#ifdef INFLATE_CLEAR_UNUSED_UNDEFINED
+        /* Copies from the overflow portion of this buffer are undefined and
+           may cause analysis tools to raise a warning if we don't initialize
+           it.  However, this undefined data overwrites other undefined data
+           and is subsequently either overwritten or left deliberately
+           undefined at the end of decode; so there's really no point.
+         */
+        memset(state->window + wsize, 0, CHUNKCOPY_CHUNK_SIZE);
+#endif
+    }
+
+    /* if window not in use yet, initialize */
+    if (state->wsize == 0) {
+        state->wsize = 1U << state->wbits;
+        state->wnext = 0;
+        state->whave = 0;
+    }
+
+    /* copy state->wsize or less output bytes into the circular window */
+    if (copy >= state->wsize) {
+        zmemcpy(state->window, end - state->wsize, state->wsize);
+        state->wnext = 0;
+        state->whave = state->wsize;
+    }
+    else {
+        dist = state->wsize - state->wnext;
+        if (dist > copy) dist = copy;
+        zmemcpy(state->window + state->wnext, end - copy, dist);
+        copy -= dist;
+        if (copy) {
+            zmemcpy(state->window, end - copy, copy);
+            state->wnext = copy;
+            state->whave = state->wsize;
+        }
+        else {
+            state->wnext += dist;
+            if (state->wnext == state->wsize) state->wnext = 0;
+            if (state->whave < state->wsize) state->whave += dist;
+        }
+    }
+    return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+#  define UPDATE(check, buf, len) \
+    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+#  define CRC2(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        check = crc32(check, hbuf, 2); \
+    } while (0)
+
+#  define CRC4(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        hbuf[2] = (unsigned char)((word) >> 16); \
+        hbuf[3] = (unsigned char)((word) >> 24); \
+        check = crc32(check, hbuf, 4); \
+    } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+   if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        if (have == 0) goto inf_leave; \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/*
+   inflate() uses a state machine to process as much input data and generate as
+   much output data as possible before returning.  The state machine is
+   structured roughly as follows:
+
+    for (;;) switch (state) {
+    ...
+    case STATEn:
+        if (not enough input data or output space to make progress)
+            return;
+        ... make progress ...
+        state = STATEm;
+        break;
+    ...
+    }
+
+   so when inflate() is called again, the same case is attempted again, and
+   if the appropriate resources are provided, the machine proceeds to the
+   next state.  The NEEDBITS() macro is usually the way the state evaluates
+   whether it can proceed or should return.  NEEDBITS() does the return if
+   the requested bits are not available.  The typical use of the BITS macros
+   is:
+
+        NEEDBITS(n);
+        ... do something with BITS(n) ...
+        DROPBITS(n);
+
+   where NEEDBITS(n) either returns from inflate() if there isn't enough
+   input left to load n bits into the accumulator, or it continues.  BITS(n)
+   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
+   the low n bits off the accumulator.  INITBITS() clears the accumulator
+   and sets the number of available bits to zero.  BYTEBITS() discards just
+   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
+   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+   if there is no input available.  The decoding of variable length codes uses
+   PULLBYTE() directly in order to pull just enough bytes to decode the next
+   code, and no more.
+
+   Some states loop until they get enough input, making sure that enough
+   state information is maintained to continue the loop where it left off
+   if NEEDBITS() returns in the loop.  For example, want, need, and keep
+   would all have to actually be part of the saved state in case NEEDBITS()
+   returns:
+
+    case STATEw:
+        while (want < need) {
+            NEEDBITS(n);
+            keep[want++] = BITS(n);
+            DROPBITS(n);
+        }
+        state = STATEx;
+    case STATEx:
+
+   As shown above, if the next state is also the next case, then the break
+   is omitted.
+
+   A state may also return if there is not enough output space available to
+   complete that state.  Those states are copying stored data, writing a
+   literal byte, and copying a matching string.
+
+   When returning, a "goto inf_leave" is used to update the total counters,
+   update the check value, and determine whether any progress has been made
+   during that inflate() call in order to return the proper return code.
+   Progress is defined as a change in either strm->avail_in or strm->avail_out.
+   When there is a window, goto inf_leave will update the window with the last
+   output written.  If a goto inf_leave occurs in the middle of decompression
+   and there is no window currently, goto inf_leave will create one and copy
+   output to the window for the next call of inflate().
+
+   In this implementation, the flush parameter of inflate() only affects the
+   return code (per zlib.h).  inflate() always writes as much as possible to
+   strm->next_out, given the space available and the provided input--the effect
+   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
+   the allocation of and copying into a sliding window until necessary, which
+   provides the effect documented in zlib.h for Z_FINISH when the entire input
+   stream available.  So the only thing the flush parameter actually does is:
+   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
+   will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+    struct inflate_state FAR *state;
+    z_const unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned in, out;           /* save starting available input and output */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code here;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+#ifdef GUNZIP
+    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
+#endif
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    if (inflateStateCheck(strm) || strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0))
+        return Z_STREAM_ERROR;
+
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
+    LOAD();
+    in = have;
+    out = left;
+    ret = Z_OK;
+    for (;;)
+        switch (state->mode) {
+        case HEAD:
+            if (state->wrap == 0) {
+                state->mode = TYPEDO;
+                break;
+            }
+            NEEDBITS(16);
+#ifdef GUNZIP
+            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
+                if (state->wbits == 0)
+                    state->wbits = 15;
+                state->check = crc32(0L, Z_NULL, 0);
+                CRC2(state->check, hold);
+                INITBITS();
+                state->mode = FLAGS;
+                break;
+            }
+            state->flags = 0;           /* expect zlib header */
+            if (state->head != Z_NULL)
+                state->head->done = -1;
+            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
+#else
+            if (
+#endif
+                ((BITS(8) << 8) + (hold >> 8)) % 31) {
+                strm->msg = (char *)"incorrect header check";
+                state->mode = BAD;
+                break;
+            }
+            if (BITS(4) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            DROPBITS(4);
+            len = BITS(4) + 8;
+            if (state->wbits == 0)
+                state->wbits = len;
+            if (len > 15 || len > state->wbits) {
+                strm->msg = (char *)"invalid window size";
+                state->mode = BAD;
+                break;
+            }
+            state->dmax = 1U << len;
+            Tracev((stderr, "inflate:   zlib header ok\n"));
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = hold & 0x200 ? DICTID : TYPE;
+            INITBITS();
+            break;
+#ifdef GUNZIP
+        case FLAGS:
+            NEEDBITS(16);
+            state->flags = (int)(hold);
+            if ((state->flags & 0xff) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            if (state->flags & 0xe000) {
+                strm->msg = (char *)"unknown header flags set";
+                state->mode = BAD;
+                break;
+            }
+            if (state->head != Z_NULL)
+                state->head->text = (int)((hold >> 8) & 1);
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC2(state->check, hold);
+            INITBITS();
+            state->mode = TIME;
+        case TIME:
+            NEEDBITS(32);
+            if (state->head != Z_NULL)
+                state->head->time = hold;
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC4(state->check, hold);
+            INITBITS();
+            state->mode = OS;
+        case OS:
+            NEEDBITS(16);
+            if (state->head != Z_NULL) {
+                state->head->xflags = (int)(hold & 0xff);
+                state->head->os = (int)(hold >> 8);
+            }
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC2(state->check, hold);
+            INITBITS();
+            state->mode = EXLEN;
+        case EXLEN:
+            if (state->flags & 0x0400) {
+                NEEDBITS(16);
+                state->length = (unsigned)(hold);
+                if (state->head != Z_NULL)
+                    state->head->extra_len = (unsigned)hold;
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    CRC2(state->check, hold);
+                INITBITS();
+            }
+            else if (state->head != Z_NULL)
+                state->head->extra = Z_NULL;
+            state->mode = EXTRA;
+        case EXTRA:
+            if (state->flags & 0x0400) {
+                copy = state->length;
+                if (copy > have) copy = have;
+                if (copy) {
+                    if (state->head != Z_NULL &&
+                        state->head->extra != Z_NULL) {
+                        len = state->head->extra_len - state->length;
+                        zmemcpy(state->head->extra + len, next,
+                                len + copy > state->head->extra_max ?
+                                state->head->extra_max - len : copy);
+                    }
+                    if ((state->flags & 0x0200) && (state->wrap & 4))
+                        state->check = crc32(state->check, next, copy);
+                    have -= copy;
+                    next += copy;
+                    state->length -= copy;
+                }
+                if (state->length) goto inf_leave;
+            }
+            state->length = 0;
+            state->mode = NAME;
+        case NAME:
+            if (state->flags & 0x0800) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->name != Z_NULL &&
+                            state->length < state->head->name_max)
+                        state->head->name[state->length++] = (Bytef)len;
+                } while (len && copy < have);
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->name = Z_NULL;
+            state->length = 0;
+            state->mode = COMMENT;
+        case COMMENT:
+            if (state->flags & 0x1000) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->comment != Z_NULL &&
+                            state->length < state->head->comm_max)
+                        state->head->comment[state->length++] = (Bytef)len;
+                } while (len && copy < have);
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->comment = Z_NULL;
+            state->mode = HCRC;
+        case HCRC:
+            if (state->flags & 0x0200) {
+                NEEDBITS(16);
+                if ((state->wrap & 4) && hold != (state->check & 0xffff)) {
+                    strm->msg = (char *)"header crc mismatch";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+            }
+            if (state->head != Z_NULL) {
+                state->head->hcrc = (int)((state->flags >> 9) & 1);
+                state->head->done = 1;
+            }
+            strm->adler = state->check = crc32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+            break;
+#endif
+        case DICTID:
+            NEEDBITS(32);
+            strm->adler = state->check = ZSWAP32(hold);
+            INITBITS();
+            state->mode = DICT;
+        case DICT:
+            if (state->havedict == 0) {
+                RESTORE();
+                return Z_NEED_DICT;
+            }
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+        case TYPE:
+            if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+        case TYPEDO:
+            if (state->last) {
+                BYTEBITS();
+                state->mode = CHECK;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN_;             /* decode codes */
+                if (flush == Z_TREES) {
+                    DROPBITS(2);
+                    goto inf_leave;
+                }
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+        case STORED:
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+            state->mode = COPY_;
+            if (flush == Z_TREES) goto inf_leave;
+        case COPY_:
+            state->mode = COPY;
+        case COPY:
+            copy = state->length;
+            if (copy) {
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                if (copy == 0) goto inf_leave;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+                break;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+        case TABLE:
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+            state->have = 0;
+            state->mode = LENLENS;
+        case LENLENS:
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (const code FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+            state->have = 0;
+            state->mode = CODELENS;
+        case CODELENS:
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    here = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (here.val < 16) {
+                    DROPBITS(here.bits);
+                    state->lens[state->have++] = here.val;
+                }
+                else {
+                    if (here.val == 16) {
+                        NEEDBITS(here.bits + 2);
+                        DROPBITS(here.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = state->lens[state->have - 1];
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (here.val == 17) {
+                        NEEDBITS(here.bits + 3);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(here.bits + 7);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* check for end-of-block code (better have one) */
+            if (state->lens[256] == 0) {
+                strm->msg = (char *)"invalid code -- missing end-of-block";
+                state->mode = BAD;
+                break;
+            }
+
+            /* build code tables -- note: do not change the lenbits or distbits
+               values here (9 and 6) without reading the comments in inftrees.h
+               concerning the ENOUGH constants, which depend on those values */
+            state->next = state->codes;
+            state->lencode = (const code FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (const code FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN_;
+            if (flush == Z_TREES) goto inf_leave;
+        case LEN_:
+            state->mode = LEN;
+        case LEN:
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                inflate_fast(strm, out);
+                LOAD();
+                if (state->mode == TYPE)
+                    state->back = -1;
+                break;
+            }
+            state->back = 0;
+            for (;;) {
+                here = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (here.op && (here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+                state->back += last.bits;
+            }
+            DROPBITS(here.bits);
+            state->back += here.bits;
+            state->length = (unsigned)here.val;
+            if ((int)(here.op) == 0) {
+                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", here.val));
+                state->mode = LIT;
+                break;
+            }
+            if (here.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->back = -1;
+                state->mode = TYPE;
+                break;
+            }
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+            state->extra = (unsigned)(here.op) & 15;
+            state->mode = LENEXT;
+        case LENEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+                state->back += state->extra;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+            state->was = state->length;
+            state->mode = DIST;
+        case DIST:
+            for (;;) {
+                here = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+                state->back += last.bits;
+            }
+            DROPBITS(here.bits);
+            state->back += here.bits;
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)here.val;
+            state->extra = (unsigned)(here.op) & 15;
+            state->mode = DISTEXT;
+        case DISTEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+                state->back += state->extra;
+            }
+#ifdef INFLATE_STRICT
+            if (state->offset > state->dmax) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+            state->mode = MATCH;
+        case MATCH:
+            if (left == 0) goto inf_leave;
+            copy = out - left;
+            if (state->offset > copy) {         /* copy from window */
+                copy = state->offset - copy;
+                if (copy > state->whave) {
+                    if (state->sane) {
+                        strm->msg = (char *)"invalid distance too far back";
+                        state->mode = BAD;
+                        break;
+                    }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+                    Trace((stderr, "inflate.c too far\n"));
+                    copy -= state->whave;
+                    if (copy > state->length) copy = state->length;
+                    if (copy > left) copy = left;
+                    left -= copy;
+                    state->length -= copy;
+                    do {
+                        *put++ = 0;
+                    } while (--copy);
+                    if (state->length == 0) state->mode = LEN;
+                    break;
+#endif
+                }
+                if (copy > state->wnext) {
+                    copy -= state->wnext;
+                    from = state->window + (state->wsize - copy);
+                }
+                else
+                    from = state->window + (state->wnext - copy);
+                if (copy > state->length) copy = state->length;
+                if (copy > left) copy = left;
+                put = chunkcopy_safe(put, from, copy, put + left);
+            }
+            else {                              /* copy from output */
+                copy = state->length;
+                if (copy > left) copy = left;
+                put = chunkcopy_lapped_safe(put, state->offset, copy, put + left);
+            }
+            left -= copy;
+            state->length -= copy;
+            if (state->length == 0) state->mode = LEN;
+            break;
+        case LIT:
+            if (left == 0) goto inf_leave;
+            *put++ = (unsigned char)(state->length);
+            left--;
+            state->mode = LEN;
+            break;
+        case CHECK:
+            if (state->wrap) {
+                NEEDBITS(32);
+                out -= left;
+                strm->total_out += out;
+                state->total += out;
+                if ((state->wrap & 4) && out)
+                    strm->adler = state->check =
+                        UPDATE(state->check, put - out, out);
+                out = left;
+                if ((state->wrap & 4) && (
+#ifdef GUNZIP
+                     state->flags ? hold :
+#endif
+                     ZSWAP32(hold)) != state->check) {
+                    strm->msg = (char *)"incorrect data check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   check matches trailer\n"));
+            }
+#ifdef GUNZIP
+            state->mode = LENGTH;
+        case LENGTH:
+            if (state->wrap && state->flags) {
+                NEEDBITS(32);
+                if (hold != (state->total & 0xffffffffUL)) {
+                    strm->msg = (char *)"incorrect length check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   length matches trailer\n"));
+            }
+#endif
+            state->mode = DONE;
+        case DONE:
+            ret = Z_STREAM_END;
+            goto inf_leave;
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+        case MEM:
+            return Z_MEM_ERROR;
+        case SYNC:
+        default:
+            return Z_STREAM_ERROR;
+        }
+
+    /*
+       Return from inflate(), updating the total counts and the check value.
+       If there was no progress during the inflate() call, return a buffer
+       error.  Call updatewindow() to create and/or update the window state.
+       Note: a memory error from inflate() is non-recoverable.
+     */
+  inf_leave:
+    RESTORE();
+    if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
+            (state->mode < CHECK || flush != Z_FINISH)))
+        if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
+            state->mode = MEM;
+            return Z_MEM_ERROR;
+        }
+    in -= strm->avail_in;
+    out -= strm->avail_out;
+    strm->total_in += in;
+    strm->total_out += out;
+    state->total += out;
+    if ((state->wrap & 4) && out)
+        strm->adler = state->check =
+            UPDATE(state->check, strm->next_out - out, out);
+    strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
+                      (state->mode == TYPE ? 128 : 0) +
+                      (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
+    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+        ret = Z_BUF_ERROR;
+    return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (inflateStateCheck(strm))
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->window != Z_NULL) ZFREE(strm, state->window);
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+Bytef *dictionary;
+uInt *dictLength;
+{
+    struct inflate_state FAR *state;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* copy dictionary */
+    if (state->whave && dictionary != Z_NULL) {
+        zmemcpy(dictionary, state->window + state->wnext,
+                state->whave - state->wnext);
+        zmemcpy(dictionary + state->whave - state->wnext,
+                state->window, state->wnext);
+    }
+    if (dictLength != Z_NULL)
+        *dictLength = state->whave;
+    return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+    struct inflate_state FAR *state;
+    unsigned long dictid;
+    int ret;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->wrap != 0 && state->mode != DICT)
+        return Z_STREAM_ERROR;
+
+    /* check for correct dictionary identifier */
+    if (state->mode == DICT) {
+        dictid = adler32(0L, Z_NULL, 0);
+        dictid = adler32(dictid, dictionary, dictLength);
+        if (dictid != state->check)
+            return Z_DATA_ERROR;
+    }
+
+    /* copy dictionary to window using updatewindow(), which will amend the
+       existing dictionary if appropriate */
+    ret = updatewindow(strm, dictionary + dictLength, dictLength);
+    if (ret) {
+        state->mode = MEM;
+        return Z_MEM_ERROR;
+    }
+    state->havedict = 1;
+    Tracev((stderr, "inflate:   dictionary set\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+    struct inflate_state FAR *state;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+    /* save header structure */
+    state->head = head;
+    head->done = 0;
+    return Z_OK;
+}
+
+/*
+   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
+   or when out of input.  When called, *have is the number of pattern bytes
+   found in order so far, in 0..3.  On return *have is updated to the new
+   state.  If on return *have equals four, then the pattern was found and the
+   return value is how many bytes were read including the last byte of the
+   pattern.  If *have is less than four, then the pattern has not been found
+   yet and the return value is len.  In the latter case, syncsearch() can be
+   called again with more data and the *have state.  *have is initialized to
+   zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+const unsigned char FAR *buf;
+unsigned len;
+{
+    unsigned got;
+    unsigned next;
+
+    got = *have;
+    next = 0;
+    while (next < len && got < 4) {
+        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+            got++;
+        else if (buf[next])
+            got = 0;
+        else
+            got = 4 - got;
+        next++;
+    }
+    *have = got;
+    return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+    unsigned len;               /* number of bytes to look at or looked at */
+    unsigned long in, out;      /* temporary to save total_in and total_out */
+    unsigned char buf[4];       /* to restore bit buffer to byte string */
+    struct inflate_state FAR *state;
+
+    /* check parameters */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+    /* if first time, start search in bit buffer */
+    if (state->mode != SYNC) {
+        state->mode = SYNC;
+        state->hold <<= state->bits & 7;
+        state->bits -= state->bits & 7;
+        len = 0;
+        while (state->bits >= 8) {
+            buf[len++] = (unsigned char)(state->hold);
+            state->hold >>= 8;
+            state->bits -= 8;
+        }
+        state->have = 0;
+        syncsearch(&(state->have), buf, len);
+    }
+
+    /* search available input */
+    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+    strm->avail_in -= len;
+    strm->next_in += len;
+    strm->total_in += len;
+
+    /* return no joy or set up to restart inflate() on a new block */
+    if (state->have != 4) return Z_DATA_ERROR;
+    in = strm->total_in;  out = strm->total_out;
+    inflateReset(strm);
+    strm->total_in = in;  strm->total_out = out;
+    state->mode = TYPE;
+    return Z_OK;
+}
+
+/*
+   Returns true if inflate is currently at the end of a block generated by
+   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+   implementation to provide an additional safety check. PPP uses
+   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+   block. When decompressing, PPP checks that at the end of input packet,
+   inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+    struct inflate_state FAR *state;
+    struct inflate_state FAR *copy;
+    unsigned char FAR *window;
+    unsigned wsize;
+
+    /* check input */
+    if (inflateStateCheck(source) || dest == Z_NULL)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)source->state;
+
+    /* allocate space */
+    copy = (struct inflate_state FAR *)
+           ZALLOC(source, 1, sizeof(struct inflate_state));
+    if (copy == Z_NULL) return Z_MEM_ERROR;
+    window = Z_NULL;
+    if (state->window != Z_NULL) {
+        window = (unsigned char FAR *)
+                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+        if (window == Z_NULL) {
+            ZFREE(source, copy);
+            return Z_MEM_ERROR;
+        }
+    }
+
+    /* copy state */
+    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
+    copy->strm = dest;
+    if (state->lencode >= state->codes &&
+        state->lencode <= state->codes + ENOUGH - 1) {
+        copy->lencode = copy->codes + (state->lencode - state->codes);
+        copy->distcode = copy->codes + (state->distcode - state->codes);
+    }
+    copy->next = copy->codes + (state->next - state->codes);
+    if (window != Z_NULL) {
+        wsize = 1U << state->wbits;
+        zmemcpy(window, state->window, wsize);
+    }
+    copy->window = window;
+    dest->state = (struct internal_state FAR *)copy;
+    return Z_OK;
+}
+
+int ZEXPORT inflateUndermine(strm, subvert)
+z_streamp strm;
+int subvert;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+    state->sane = !subvert;
+    return Z_OK;
+#else
+    (void)subvert;
+    state->sane = 1;
+    return Z_DATA_ERROR;
+#endif
+}
+
+int ZEXPORT inflateValidate(strm, check)
+z_streamp strm;
+int check;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (check)
+        state->wrap |= 4;
+    else
+        state->wrap &= ~4;
+    return Z_OK;
+}
+
+long ZEXPORT inflateMark(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm))
+        return -(1L << 16);
+    state = (struct inflate_state FAR *)strm->state;
+    return (long)(((unsigned long)((long)state->back)) << 16) +
+        (state->mode == COPY ? state->length :
+            (state->mode == MATCH ? state->was - state->length : 0));
+}
+
+unsigned long ZEXPORT inflateCodesUsed(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (inflateStateCheck(strm)) return (unsigned long)-1;
+    state = (struct inflate_state FAR *)strm->state;
+    return (unsigned long)(state->next - state->codes);
+}
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index e42891d..ff89d5a 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -61,8 +61,8 @@
       "android_webview/java/strings/android_webview_strings.grd",
       "chrome/android/java/strings/android_chrome_strings.grd",
       "chrome/android/webapk/strings/android_webapk_strings.grd",
-      "components/autofill/android/autofill_strings.grd",
-      "components/web_contents_delegate_android/web_contents_delegate_android_strings.grd",
+      "components/autofill/android/java/strings/autofill_strings.grd",
+      "components/web_contents_delegate_android/java/strings/web_contents_delegate_android_strings.grd",
       "content/public/android/java/strings/android_content_strings.grd",
       "ui/android/java/strings/android_ui_strings.grd",
     ],
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index ddf911d..e901db8 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -738,84 +738,6 @@
 };
 
 template <>
-struct FuzzTraits<content::SyntheticGesturePacket> {
-  static bool Fuzz(content::SyntheticGesturePacket* p,
-                       Fuzzer* fuzzer) {
-    // TODO(mbarbella): Support mutation.
-    if (!fuzzer->ShouldGenerate())
-      return true;
-
-    std::unique_ptr<content::SyntheticGestureParams> gesture_params;
-    switch (RandInRange(
-        content::SyntheticGestureParams::SYNTHETIC_GESTURE_TYPE_MAX + 1)) {
-      case content::SyntheticGestureParams::GestureType::
-          SMOOTH_SCROLL_GESTURE: {
-        content::SyntheticSmoothScrollGestureParams* params =
-            new content::SyntheticSmoothScrollGestureParams();
-        if (!FuzzParam(&params->anchor, fuzzer))
-          return false;
-        if (!FuzzParam(&params->distances, fuzzer))
-          return false;
-        if (!FuzzParam(&params->prevent_fling, fuzzer))
-          return false;
-        if (!FuzzParam(&params->speed_in_pixels_s, fuzzer))
-          return false;
-        gesture_params.reset(params);
-        break;
-      }
-      case content::SyntheticGestureParams::GestureType::SMOOTH_DRAG_GESTURE: {
-        content::SyntheticSmoothDragGestureParams* params =
-            new content::SyntheticSmoothDragGestureParams();
-        if (!FuzzParam(&params->start_point, fuzzer))
-          return false;
-        if (!FuzzParam(&params->distances, fuzzer))
-          return false;
-        if (!FuzzParam(&params->speed_in_pixels_s, fuzzer))
-          return false;
-        gesture_params.reset(params);
-        break;
-      }
-      case content::SyntheticGestureParams::GestureType::PINCH_GESTURE: {
-        content::SyntheticPinchGestureParams* params =
-            new content::SyntheticPinchGestureParams();
-        if (!FuzzParam(&params->scale_factor, fuzzer))
-          return false;
-        if (!FuzzParam(&params->anchor, fuzzer))
-          return false;
-        if (!FuzzParam(&params->relative_pointer_speed_in_pixels_s,
-                           fuzzer))
-          return false;
-        gesture_params.reset(params);
-        break;
-      }
-      case content::SyntheticGestureParams::GestureType::TAP_GESTURE: {
-        content::SyntheticTapGestureParams* params =
-            new content::SyntheticTapGestureParams();
-        if (!FuzzParam(&params->position, fuzzer))
-          return false;
-        if (!FuzzParam(&params->duration_ms, fuzzer))
-          return false;
-        gesture_params.reset(params);
-        break;
-      }
-      case content::SyntheticGestureParams::GestureType::POINTER_ACTION_LIST: {
-        std::vector<content::SyntheticPointerActionListParams::ParamList>
-            param_list;
-        if (!FuzzParam(&param_list, fuzzer))
-          return false;
-        content::SyntheticPointerActionListParams* params =
-            new content::SyntheticPointerActionListParams();
-        params->params = param_list;
-        gesture_params.reset(params);
-        break;
-      }
-    }
-    p->set_gesture_params(std::move(gesture_params));
-    return true;
-  }
-};
-
-template <>
 struct FuzzTraits<content::WebCursor> {
   static bool Fuzz(content::WebCursor* p, Fuzzer* fuzzer) {
     content::CursorInfo info;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 73b8d36..2ad57d7 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -20617,6 +20617,24 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.Bindings.NativeBindingCreationTime"
+    units="microseconds">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <summary>
+    The elapsed time to create a new full instance of an extension API's
+    bindings using native bindings.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.Bindings.UpdateBindingsForContextTime"
+    units="microseconds">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <summary>
+    The elapsed time to update the bindings for a new or existing v8::Context.
+    The suffix indicates which type of context the bindings are for.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.BookmarkAppLaunchContainer"
     enum="AppLaunchContainer">
   <owner>benwells@chromium.org</owner>
@@ -40041,6 +40059,9 @@
 
 <histogram name="Net.JobControllerSet.CountOfNonPreconnectAltJob"
     units="alt_jobs">
+  <obsolete>
+    Deprecated 08/2017.
+  </obsolete>
   <owner>zhongyi@chromium.org</owner>
   <summary>
     This counts number of alternative jobs which are still alive.
@@ -40049,6 +40070,9 @@
 
 <histogram name="Net.JobControllerSet.CountOfNonPreconnectMainJob"
     units="main_jobs">
+  <obsolete>
+    Deprecated 08/2017.
+  </obsolete>
   <owner>zhongyi@chromium.org</owner>
   <summary>This counts number of main jobs which are still alive.</summary>
 </histogram>
@@ -41015,6 +41039,15 @@
   </summary>
 </histogram>
 
+<histogram name="Net.PushedStreamAlreadyHasResponseHeaders" enum="Boolean">
+  <owner>bnc@chromium.org</owner>
+  <summary>
+    Records whether HTTP/2 response headers have already arrived on a pushed
+    stream at the time the stream is matched up with a request. See
+    https://crbug.com/554220.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicActiveSessions">
   <owner>rch@chromium.org</owner>
   <summary>
@@ -94241,6 +94274,19 @@
       name="RendererScheduler.QueueingDurationWhenExpectedQueueingTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ExtensionContextType" separator=".">
+  <suffix name="BlessedExtensionContext" label="Blessed Extension Context"/>
+  <suffix name="BlessedWebPageContext" label="Blessed Web Page Context"/>
+  <suffix name="ContentScriptContext" label="Content Script Context"/>
+  <suffix name="LockScreenExtensionContext"
+      label="Lock Screen Extension Context"/>
+  <suffix name="WebPageContext" label="(unblessed) Web Page Context"/>
+  <suffix name="ServiceWorkerContext" label="Service Worker Context"/>
+  <suffix name="UnblessedExtensionContext" label="Unblessed Extension Context"/>
+  <suffix name="WebUIContext" label="WebUI Context"/>
+  <affected-histogram name="Extensions.Bindings.UpdateBindingsForContextTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="ExtensionFunctionExecutionTime" separator=".">
   <suffix name="1msTo5ms"
       label="Execution took between 1ms and 5ms (tolerable)."/>
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 5ab0fd6..076628e 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -148,8 +148,9 @@
       prominent_colors[static_cast<int>(ColorProfileType::DARK_MUTED)];
   if (SK_ColorTRANSPARENT == dark_muted)
     return app_list::AppListView::kDefaultBackgroundColor;
-  return color_utils::AlphaBlend(SK_ColorBLACK, dark_muted,
-                                 app_list::AppListView::kDarkMutedBlendAlpha);
+  return color_utils::GetResultingPaintColor(
+      SkColorSetA(SK_ColorBLACK, AppListView::kAppListColorDarkenAlpha),
+      dark_muted);
 }
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kExcludeWindowFromEventHandling, false);
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index f6ae8988..2534b91 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -60,7 +60,7 @@
   static constexpr float kAppListOpacityWithBlur = 0.7;
 
   // The preferred blend alpha with wallpaper color for background.
-  static constexpr int kDarkMutedBlendAlpha = 0x3f;
+  static constexpr int kAppListColorDarkenAlpha = 178;
 
   // The defualt color of the app list background.
   static constexpr SkColor kDefaultBackgroundColor = SK_ColorBLACK;
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index d3535f0..cba9fbd6 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -27,6 +27,8 @@
 
 namespace views {
 
+constexpr int kFocusRingThicknessDip = 2;
+
 // View used to paint the focus ring around the Checkbox icon.
 // The icon is painted separately.
 class IconFocusRing : public View {
@@ -50,7 +52,7 @@
 
 void IconFocusRing::Layout() {
   gfx::Rect focus_bounds = checkbox_->image()->bounds();
-  focus_bounds.Inset(gfx::Insets(-2.f));
+  focus_bounds.Inset(-kFocusRingThicknessDip, -kFocusRingThicknessDip);
   SetBoundsRect(focus_bounds);
 }
 
@@ -248,7 +250,9 @@
 void Checkbox::PaintFocusRing(View* view,
                               gfx::Canvas* canvas,
                               const cc::PaintFlags& flags) {
-  canvas->DrawRoundRect(view->GetLocalBounds(), 2.f, flags);
+  gfx::RectF bounds(view->GetLocalBounds());
+  bounds.Inset(kFocusRingThicknessDip, kFocusRingThicknessDip);
+  canvas->DrawRoundRect(bounds, kFocusRingThicknessDip, flags);
 }
 
 const gfx::VectorIcon& Checkbox::GetVectorIcon() const {
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
index 98bc7ed..80e7d76 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
@@ -27,6 +27,7 @@
         border: 2px solid rgba(66, 133, 244, 0.6);
         box-shadow: 0 2px 4px rgba(0, 0, 0, 0.17);
         margin: 4px;
+        outline: none;
       }
 
       iron-icon {
@@ -51,7 +52,7 @@
       }
     </style>
 
-    <div id="container" tabindex="0">
+    <div id="container">
       <iron-a11y-keys keys="up down left right space enter"
           on-keys-pressed="onKeysPressed_">
       </iron-a11y-keys>
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
index 46a462b..990aaf67 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
@@ -39,6 +39,7 @@
     selectedItem: {
       type: Object,
       notify: true,
+      observer: 'onImageSelected_',
     },
 
     /**
@@ -92,7 +93,21 @@
   fallbackImage_: null,
 
   setFocus: function() {
-    this.$.container.focus();
+    if (this.selectedItem) {
+      this.selectedItem.focus();
+    }
+  },
+
+  onImageSelected_: function(newImg, oldImg) {
+    if (newImg) {
+      newImg.setAttribute('tabindex', '0');
+      newImg.setAttribute('aria-checked', 'true');
+      newImg.focus();
+    }
+    if (oldImg) {
+      oldImg.removeAttribute('tabindex');
+      oldImg.removeAttribute('aria-checked');
+    }
   },
 
   /**