diff --git a/DEPS b/DEPS
index e1445de..092c81a 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # 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': 'a5838cc92b08746ec9ebbb64f4ca2a1d44435491',
+  'v8_revision': '5ac0930fac8e3478d7208f942bbe4fd2b43a203d',
   # 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.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ceb07cf9dce01b0b83e76f1d8b6bcbf25b484f4e',
+  'catapult_revision': '39c0bfa71a69a3c7a16ef674c82e2877fb64cc75',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -890,6 +890,8 @@
     'name': 'fetch_telemetry_binary_dependencies',
     'pattern': '.',
     'action': [ 'python',
+                'src/tools/perf/conditionally_execute',
+                '--gyp-condition', 'fetch_telemetry_dependencies=1',
                 'src/third_party/catapult/telemetry/bin/fetch_telemetry_binary_dependencies',
     ],
   },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2752765..79b3f70a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -331,14 +331,6 @@
       (),
     ),
     (
-      'base::NonThreadSafe',
-      (
-        'base::NonThreadSafe is deprecated.',
-      ),
-      True,
-      (),
-    ),
-    (
       'base::SequenceChecker',
       (
         'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 229e4c2..3b2c170 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -1081,42 +1081,21 @@
 }
 
 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
-  bool horizontal = shelf_->IsHorizontalAlignment();
   bool should_change = false;
   if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
     // The visibility of the shelf changes only if the shelf was dragged X%
     // along the correct axis. If the shelf was already visible, then the
     // direction of the drag does not matter.
     const float kDragHideThreshold = 0.4f;
-    gfx::Rect bounds = GetIdealBounds();
-    float drag_ratio = fabs(gesture_drag_amount_) /
-                       (horizontal ? bounds.height() : bounds.width());
-    if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
-      should_change = drag_ratio > kDragHideThreshold;
-    } else {
-      bool correct_direction = false;
-      switch (shelf_->alignment()) {
-        case SHELF_ALIGNMENT_BOTTOM:
-        case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-        case SHELF_ALIGNMENT_RIGHT:
-          correct_direction = gesture_drag_amount_ < 0;
-          break;
-        case SHELF_ALIGNMENT_LEFT:
-          correct_direction = gesture_drag_amount_ > 0;
-          break;
-      }
-      should_change = correct_direction && drag_ratio > kDragHideThreshold;
-    }
+    const gfx::Rect bounds = GetIdealBounds();
+    const float drag_ratio =
+        fabs(gesture_drag_amount_) /
+        (shelf_->IsHorizontalAlignment() ? bounds.height() : bounds.width());
+
+    should_change =
+        IsSwipingCorrectDirection() && drag_ratio > kDragHideThreshold;
   } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
-    if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
-      should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0
-                                 : fabs(gesture.details().velocity_x()) > 0;
-    } else {
-      should_change =
-          SelectValueForShelfAlignment(gesture.details().velocity_y() < 0,
-                                       gesture.details().velocity_x() > 0,
-                                       gesture.details().velocity_x() < 0);
-    }
+    should_change = IsSwipingCorrectDirection();
   } else {
     NOTREACHED();
   }
@@ -1159,4 +1138,20 @@
   gesture_drag_status_ = GESTURE_DRAG_NONE;
 }
 
+bool ShelfLayoutManager::IsSwipingCorrectDirection() {
+  switch (shelf_->alignment()) {
+    case SHELF_ALIGNMENT_BOTTOM:
+    case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+    case SHELF_ALIGNMENT_RIGHT:
+      if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN)
+        return gesture_drag_amount_ > 0;
+      return gesture_drag_amount_ < 0;
+    case SHELF_ALIGNMENT_LEFT:
+      if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN)
+        return gesture_drag_amount_ < 0;
+      return gesture_drag_amount_ > 0;
+  }
+  return false;
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index b7e68fa..7e66fb2 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -295,6 +295,10 @@
   void CompleteGestureDrag(const ui::GestureEvent& gesture);
   void CancelGestureDrag();
 
+  // Returns true if the gesture is swiping up on a hidden shelf or swiping down
+  // on a visible shelf; other gestures should not change shelf visibility.
+  bool IsSwipingCorrectDirection();
+
   // True when inside UpdateBoundsAndOpacity() method. Used to prevent calling
   // UpdateBoundsAndOpacity() again from SetChildBounds().
   bool updating_bounds_;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 0bfea3d7..a6e92da 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -369,26 +369,19 @@
   EXPECT_EQ(shelf_shown.ToString(),
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
-  // Swipe up again. The shelf should hide.
+  // Swipe up again. The shelf should stay visible.
   end = start - delta;
   generator.GestureScrollSequenceWithCallback(
       start, end, kTimeDelta, kNumScrollSteps,
       base::Bind(&ShelfDragCallback::ProcessScroll,
                  base::Unretained(&handler)));
-  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
-  EXPECT_EQ(shelf_hidden.ToString(),
+  EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
+  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
+  EXPECT_EQ(shelf_shown.ToString(),
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
 
-  // Swipe up yet again to show it.
-  end = start + delta;
-  generator.GestureScrollSequenceWithCallback(
-      end, start, kTimeDelta, kNumScrollSteps,
-      base::Bind(&ShelfDragCallback::ProcessScroll,
-                 base::Unretained(&handler)));
-
   // Swipe down very little. It shouldn't change any state.
+  end = start + delta;
   if (shelf->IsHorizontalAlignment())
     end.set_y(start.y() + shelf_shown.height() * 3 / 10);
   else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
@@ -518,9 +511,10 @@
             GetShelfWidget()->GetWindowBoundsInScreen().ToString());
   EXPECT_EQ(bounds_fullscreen.ToString(), window->bounds().ToString());
 
-  // Swipe up again. This should hide the shelf.
+  // Swipe down to hide the shelf.
+  end = start + delta;
   generator.GestureScrollSequenceWithCallback(
-      below_start, end, kTimeDelta, kNumScrollSteps,
+      start, end, kTimeDelta, kNumScrollSteps,
       base::Bind(&ShelfDragCallback::ProcessScroll,
                  base::Unretained(&handler)));
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
@@ -1516,8 +1510,9 @@
     waiter1.WaitTillDoneAnimating();
     EXPECT_TRUE(waiter1.WasValidAnimation());
 
-    // Test that the shelf animates to the auto hidden bounds after a swipe up
+    // Test that the shelf animates to the auto hidden bounds after a swipe down
     // on the visible shelf.
+    end = gfx::Point(start.x(), start.y() + 100);
     EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
     generator.GestureScrollSequence(start, end,
                                     base::TimeDelta::FromMilliseconds(10), 1);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5eb1527..0292e54 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -869,9 +869,6 @@
     "third_party/nspr/prtime.h",
     "third_party/superfasthash/superfasthash.c",
     "third_party/valgrind/memcheck.h",
-    "threading/non_thread_safe.h",
-    "threading/non_thread_safe_impl.cc",
-    "threading/non_thread_safe_impl.h",
     "threading/platform_thread.h",
     "threading/platform_thread_android.cc",
     "threading/platform_thread_internal_posix.cc",
@@ -2179,6 +2176,7 @@
     "task_scheduler/task_unittest.cc",
     "task_scheduler/test_task_factory.cc",
     "task_scheduler/test_task_factory.h",
+    "task_scheduler/test_utils.cc",
     "task_scheduler/test_utils.h",
     "template_util_unittest.cc",
     "test/histogram_tester_unittest.cc",
@@ -2190,7 +2188,6 @@
     "test/test_reg_util_win_unittest.cc",
     "test/trace_event_analyzer_unittest.cc",
     "test/user_action_tester_unittest.cc",
-    "threading/non_thread_safe_unittest.cc",
     "threading/platform_thread_unittest.cc",
     "threading/post_task_and_reply_impl_unittest.cc",
     "threading/sequenced_task_runner_handle_unittest.cc",
diff --git a/base/ios/weak_nsobject.h b/base/ios/weak_nsobject.h
index 136e430d..b1b8d10 100644
--- a/base/ios/weak_nsobject.h
+++ b/base/ios/weak_nsobject.h
@@ -11,7 +11,6 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread_checker.h"
 
 // WeakNSObject<> is patterned after scoped_nsobject<>, but instead of
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc
index 9ade6815..174446f 100644
--- a/base/task_scheduler/scheduler_worker.cc
+++ b/base/task_scheduler/scheduler_worker.cc
@@ -76,10 +76,10 @@
         continue;
       }
 
-      outer_->task_tracker_->RunTask(sequence->TakeTask(), sequence->token());
-      outer_->delegate_->DidRunTask();
+      const bool sequence_became_empty =
+          outer_->task_tracker_->RunNextTask(sequence.get());
 
-      const bool sequence_became_empty = sequence->Pop();
+      outer_->delegate_->DidRunTask();
 
       // If |sequence| isn't empty immediately after the pop, re-enqueue it to
       // maintain the invariant that a non-empty Sequence is always referenced
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 06e089b..59bbe23 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -235,10 +235,11 @@
   return true;
 }
 
-void TaskTracker::RunTask(std::unique_ptr<Task> task,
-                          const SequenceToken& sequence_token) {
+bool TaskTracker::RunNextTask(Sequence* sequence) {
+  DCHECK(sequence);
+
+  std::unique_ptr<Task> task = sequence->TakeTask();
   DCHECK(task);
-  DCHECK(sequence_token.IsValid());
 
   const TaskShutdownBehavior shutdown_behavior =
       task->traits.shutdown_behavior();
@@ -246,12 +247,14 @@
   const bool is_delayed = !task->delayed_run_time.is_null();
 
   if (can_run_task) {
-    PerformRunTask(std::move(task), sequence_token);
+    PerformRunTask(std::move(task), sequence);
     AfterRunTask(shutdown_behavior);
   }
 
   if (!is_delayed)
     DecrementNumPendingUndelayedTasks();
+
+  return sequence->Pop();
 }
 
 bool TaskTracker::HasShutdownStarted() const {
@@ -276,7 +279,7 @@
 }
 
 void TaskTracker::PerformRunTask(std::unique_ptr<Task> task,
-                                 const SequenceToken& sequence_token) {
+                                 Sequence* sequence) {
   RecordTaskLatencyHistogram(task.get());
 
   const bool previous_singleton_allowed =
@@ -289,6 +292,8 @@
       task->traits.with_base_sync_primitives());
 
   {
+    const SequenceToken& sequence_token = sequence->token();
+    DCHECK(sequence_token.IsValid());
     ScopedSetSequenceTokenForCurrentThread
         scoped_set_sequence_token_for_current_thread(sequence_token);
     ScopedSetTaskPriorityForCurrentThread
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h
index 805af65..87fc281 100644
--- a/base/task_scheduler/task_tracker.h
+++ b/base/task_scheduler/task_tracker.h
@@ -23,7 +23,6 @@
 
 class ConditionVariable;
 class HistogramBase;
-class SequenceToken;
 
 namespace internal {
 
@@ -56,11 +55,11 @@
   // this operation is allowed (|task| should be posted if-and-only-if it is).
   bool WillPostTask(const Task* task);
 
-  // Runs |task| unless the current shutdown state prevents that.
-  // |sequence_token| is the token identifying the sequence from which |task|
-  // was extracted. WillPostTask() must have allowed |task| to be posted before
-  // this is called.
-  void RunTask(std::unique_ptr<Task> task, const SequenceToken& sequence_token);
+  // Runs the next task in |sequence| unless the current shutdown state
+  // prevents that. Then, pops the task from |sequence| (even if it didn't run).
+  // Returns true if the sequence was made empty after popping the task.
+  // WillPostTask() must have allowed |task| to be posted before this is called.
+  bool RunNextTask(Sequence* sequence);
 
   // Returns true once shutdown has started (Shutdown() has been called but
   // might not have returned). Note: sequential consistency with the thread
@@ -77,11 +76,10 @@
   void SetHasShutdownStartedForTesting();
 
  protected:
-  // Runs |task|. |sequence_token| is the token identifying the sequence from
-  // which |task| was extracted. An override is expected to call its parent's
-  // implementation but is free to perform extra work before and after doing so.
-  virtual void PerformRunTask(std::unique_ptr<Task> task,
-                              const SequenceToken& sequence_token);
+  // Runs |task|. |sequence| is the sequence from which |task| was extracted.
+  // An override is expected to call its parent's implementation but
+  // is free to perform extra work before and after doing so.
+  virtual void PerformRunTask(std::unique_ptr<Task> task, Sequence* sequence);
 
 #if DCHECK_IS_ON()
   // Returns true if this context should be exempt from blocking shutdown
diff --git a/base/task_scheduler/task_tracker_posix.cc b/base/task_scheduler/task_tracker_posix.cc
index b1c1b92..73a8f359 100644
--- a/base/task_scheduler/task_tracker_posix.cc
+++ b/base/task_scheduler/task_tracker_posix.cc
@@ -15,11 +15,11 @@
 TaskTrackerPosix::~TaskTrackerPosix() = default;
 
 void TaskTrackerPosix::PerformRunTask(std::unique_ptr<Task> task,
-                                      const SequenceToken& sequence_token) {
+                                      Sequence* sequence) {
   DCHECK(watch_file_descriptor_message_loop_);
   FileDescriptorWatcher file_descriptor_watcher(
       watch_file_descriptor_message_loop_);
-  TaskTracker::PerformRunTask(std::move(task), sequence_token);
+  TaskTracker::PerformRunTask(std::move(task), sequence);
 }
 
 #if DCHECK_IS_ON()
diff --git a/base/task_scheduler/task_tracker_posix.h b/base/task_scheduler/task_tracker_posix.h
index 7a17f75..7f970c4 100644
--- a/base/task_scheduler/task_tracker_posix.h
+++ b/base/task_scheduler/task_tracker_posix.h
@@ -52,8 +52,7 @@
 
  protected:
   // TaskTracker:
-  void PerformRunTask(std::unique_ptr<Task> task,
-                      const SequenceToken& sequence_token) override;
+  void PerformRunTask(std::unique_ptr<Task> task, Sequence* sequence) override;
 
  private:
 #if DCHECK_IS_ON()
diff --git a/base/task_scheduler/task_tracker_posix_unittest.cc b/base/task_scheduler/task_tracker_posix_unittest.cc
index 69f1c2d..a10fa898 100644
--- a/base/task_scheduler/task_tracker_posix_unittest.cc
+++ b/base/task_scheduler/task_tracker_posix_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/sequence_token.h"
 #include "base/task_scheduler/task.h"
 #include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_utils.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -35,7 +36,9 @@
   tracker.set_watch_file_descriptor_message_loop(&message_loop);
 
   EXPECT_TRUE(tracker.WillPostTask(task.get()));
-  tracker.RunTask(std::move(task), SequenceToken::Create());
+
+  tracker.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
+
   EXPECT_TRUE(did_run);
 }
 
@@ -53,7 +56,8 @@
   tracker.set_watch_file_descriptor_message_loop(&message_loop);
 
   EXPECT_TRUE(tracker.WillPostTask(task.get()));
-  tracker.RunTask(std::move(task), SequenceToken::Create());
+
+  tracker.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
 
   // Run the MessageLoop to allow the read watch to be registered and
   // unregistered. This prevents a memory leak.
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
index 2fcbb66..c2d6d60 100644
--- a/base/task_scheduler/task_tracker_unittest.cc
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -27,6 +27,7 @@
 #include "base/task_scheduler/scheduler_lock.h"
 #include "base/task_scheduler/task.h"
 #include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_utils.h"
 #include "base/test/gtest_util.h"
 #include "base/test/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
@@ -113,7 +114,9 @@
     if (post_succeeded &&
         (action_ == Action::RUN || action_ == Action::WILL_POST_AND_RUN)) {
       EXPECT_TRUE(owned_task_);
-      tracker_->RunTask(std::move(owned_task_), SequenceToken::Create());
+
+      tracker_->RunNextTask(
+          test::CreateSequenceWithTask(std::move(owned_task_)).get());
     }
   }
 
@@ -255,7 +258,8 @@
 
   // Run the task.
   EXPECT_EQ(0U, NumTasksExecuted());
-  tracker_.RunTask(std::move(task), SequenceToken::Create());
+
+  tracker_.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
   EXPECT_EQ(1U, NumTasksExecuted());
 
   // Shutdown() shouldn't block.
@@ -328,12 +332,14 @@
   // should be discarded.
   EXPECT_EQ(0U, NumTasksExecuted());
   const bool should_run = GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN;
-  tracker_.RunTask(std::move(task), SequenceToken::Create());
+
+  tracker_.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
   EXPECT_EQ(should_run ? 1U : 0U, NumTasksExecuted());
   VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   // Unblock shutdown by running the remaining BLOCK_SHUTDOWN task.
-  tracker_.RunTask(std::move(block_shutdown_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(block_shutdown_task)).get());
   EXPECT_EQ(should_run ? 2U : 1U, NumTasksExecuted());
   WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 }
@@ -351,7 +357,7 @@
     VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
     // Run the task to unblock shutdown.
-    tracker_.RunTask(std::move(task), SequenceToken::Create());
+    tracker_.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
     EXPECT_EQ(1U, NumTasksExecuted());
     WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 
@@ -362,7 +368,7 @@
     WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 
     // The task shouldn't be allowed to run after shutdown.
-    tracker_.RunTask(std::move(task), SequenceToken::Create());
+    tracker_.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
     EXPECT_EQ(0U, NumTasksExecuted());
   }
 }
@@ -385,7 +391,7 @@
 
     // Run the BLOCK_SHUTDOWN task.
     EXPECT_EQ(0U, NumTasksExecuted());
-    tracker_.RunTask(std::move(task), SequenceToken::Create());
+    tracker_.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
     EXPECT_EQ(1U, NumTasksExecuted());
   } else {
     // It shouldn't be allowed to post a non BLOCK_SHUTDOWN task.
@@ -397,7 +403,8 @@
 
   // Unblock shutdown by running |block_shutdown_task|.
   VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
-  tracker_.RunTask(std::move(block_shutdown_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(block_shutdown_task)).get());
   EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
             NumTasksExecuted());
   WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
@@ -431,10 +438,11 @@
 
   // Running the task should fail iff the task isn't allowed to use singletons.
   if (can_use_singletons) {
-    tracker.RunTask(std::move(task), SequenceToken::Create());
+    tracker.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
   } else {
-    EXPECT_DCHECK_DEATH(
-        { tracker.RunTask(std::move(task), SequenceToken::Create()); });
+    EXPECT_DCHECK_DEATH({
+      tracker.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
+    });
   }
 }
 
@@ -452,7 +460,8 @@
                        }),
                        TaskTraits(MayBlock(), GetParam()), TimeDelta());
   EXPECT_TRUE(tracker.WillPostTask(task_with_may_block.get()));
-  tracker.RunTask(std::move(task_with_may_block), SequenceToken::Create());
+  tracker.RunNextTask(
+      test::CreateSequenceWithTask(std::move(task_with_may_block)).get());
 
   // Set the IO allowed bit. Expect TaskTracker to unset it before running a
   // task without the MayBlock() trait.
@@ -463,7 +472,8 @@
       }),
       TaskTraits(GetParam()), TimeDelta());
   EXPECT_TRUE(tracker.WillPostTask(task_without_may_block.get()));
-  tracker.RunTask(std::move(task_without_may_block), SequenceToken::Create());
+  tracker.RunNextTask(
+      test::CreateSequenceWithTask(std::move(task_without_may_block)).get());
 }
 
 static void RunTaskRunnerHandleVerificationTask(
@@ -477,7 +487,8 @@
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
   EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
 
-  tracker->RunTask(std::move(verify_task), SequenceToken::Create());
+  tracker->RunNextTask(
+      test::CreateSequenceWithTask(std::move(verify_task)).get());
 
   // TaskRunnerHandle state is reset outside of task's scope.
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
@@ -568,7 +579,8 @@
   VERIFY_ASYNC_FLUSH_IN_PROGRESS();
 
   // Flush() should return after the undelayed task runs.
-  tracker_.RunTask(std::move(undelayed_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(undelayed_task)).get());
   WAIT_FOR_ASYNC_FLUSH_RETURNED();
 }
 
@@ -588,14 +600,16 @@
   tracker_.WillPostTask(other_undelayed_task.get());
 
   // Run the first undelayed task.
-  tracker_.RunTask(std::move(undelayed_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(undelayed_task)).get());
 
   // Flush() shouldn't return before the second undelayed task runs.
   PlatformThread::Sleep(TestTimeouts::tiny_timeout());
   VERIFY_ASYNC_FLUSH_IN_PROGRESS();
 
   // Flush() should return after the second undelayed task runs.
-  tracker_.RunTask(std::move(other_undelayed_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(other_undelayed_task)).get());
   WAIT_FOR_ASYNC_FLUSH_RETURNED();
 }
 
@@ -615,7 +629,8 @@
   VERIFY_ASYNC_FLUSH_IN_PROGRESS();
 
   // Run the delayed task.
-  tracker_.RunTask(std::move(delayed_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(delayed_task)).get());
 
   // Flush() shouldn't return since there is still a pending undelayed
   // task.
@@ -623,7 +638,8 @@
   VERIFY_ASYNC_FLUSH_IN_PROGRESS();
 
   // Run the undelayed task.
-  tracker_.RunTask(std::move(undelayed_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(undelayed_task)).get());
 
   // Flush() should now return.
   WAIT_FOR_ASYNC_FLUSH_RETURNED();
@@ -694,14 +710,18 @@
 // Verify that SequenceToken::GetForCurrentThread() returns the Sequence's token
 // when a Task runs.
 TEST_F(TaskSchedulerTaskTrackerTest, CurrentSequenceToken) {
-  const SequenceToken sequence_token(SequenceToken::Create());
+  scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>();
+
+  const SequenceToken sequence_token = sequence->token();
   auto task =
       MakeUnique<Task>(FROM_HERE, Bind(&ExpectSequenceToken, sequence_token),
                        TaskTraits(), TimeDelta());
   tracker_.WillPostTask(task.get());
 
+  sequence->PushTask(std::move(task));
+
   EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
-  tracker_.RunTask(std::move(task), sequence_token);
+  tracker_.RunNextTask(sequence.get());
   EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
 }
 
@@ -827,7 +847,8 @@
   VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   // Unblock shutdown by running |block_shutdown_task|.
-  tracker_.RunTask(std::move(block_shutdown_task), SequenceToken::Create());
+  tracker_.RunNextTask(
+      test::CreateSequenceWithTask(std::move(block_shutdown_task)).get());
   EXPECT_EQ(kLoadTestNumIterations + 1, NumTasksExecuted());
   WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 }
@@ -851,8 +872,9 @@
         }),
         TaskTraits(), TimeDelta());
     EXPECT_TRUE(tracker.WillPostTask(task_without_sync_primitives.get()));
-    tracker.RunTask(std::move(task_without_sync_primitives),
-                    SequenceToken::Create());
+    tracker.RunNextTask(
+        test::CreateSequenceWithTask(std::move(task_without_sync_primitives))
+            .get());
 
     // Disallow waiting. Expect TaskTracker to allow it before running a task
     // with the WithBaseSyncPrimitives() trait.
@@ -864,8 +886,9 @@
                          }),
                          TaskTraits(WithBaseSyncPrimitives()), TimeDelta());
     EXPECT_TRUE(tracker.WillPostTask(task_with_sync_primitives.get()));
-    tracker.RunTask(std::move(task_with_sync_primitives),
-                    SequenceToken::Create());
+    tracker.RunNextTask(
+        test::CreateSequenceWithTask(std::move(task_with_sync_primitives))
+            .get());
   }
 
   DISALLOW_COPY_AND_ASSIGN(WaitAllowedTestThread);
@@ -924,7 +947,8 @@
     ASSERT_TRUE(tracker.WillPostTask(task.get()));
 
     HistogramTester tester;
-    tracker.RunTask(std::move(task), SequenceToken::Create());
+
+    tracker.RunNextTask(test::CreateSequenceWithTask(std::move(task)).get());
     tester.ExpectTotalCount(test.expected_histogram, 1);
   }
 }
diff --git a/base/task_scheduler/test_utils.cc b/base/task_scheduler/test_utils.cc
new file mode 100644
index 0000000..a06078f6
--- /dev/null
+++ b/base/task_scheduler/test_utils.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/test_utils.h"
+
+#include <utility>
+
+namespace base {
+namespace internal {
+namespace test {
+
+scoped_refptr<Sequence> CreateSequenceWithTask(std::unique_ptr<Task> task) {
+  scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>();
+  sequence->PushTask(std::move(task));
+  return sequence;
+}
+
+}  // namespace test
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/test_utils.h b/base/task_scheduler/test_utils.h
index dbd1227..2315768b 100644
--- a/base/task_scheduler/test_utils.h
+++ b/base/task_scheduler/test_utils.h
@@ -5,14 +5,25 @@
 #ifndef BASE_TASK_SCHEDULER_TEST_UTILS_H_
 #define BASE_TASK_SCHEDULER_TEST_UTILS_H_
 
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/task_scheduler/sequence.h"
+
 namespace base {
 namespace internal {
+
+struct Task;
+
 namespace test {
 
 // An enumeration of possible task scheduler TaskRunner types. Used to
 // parametrize relevant task_scheduler tests.
 enum class ExecutionMode { PARALLEL, SEQUENCED, SINGLE_THREADED };
 
+// Creates a Sequence and pushes |task| to it. Returns that sequence.
+scoped_refptr<Sequence> CreateSequenceWithTask(std::unique_ptr<Task> task);
+
 }  // namespace test
 }  // namespace internal
 }  // namespace base
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 8129162..4bb932e 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -59,7 +59,7 @@
 
   // internal::TaskSchedulerImpl::TaskTrackerImpl:
   void PerformRunTask(std::unique_ptr<internal::Task> task,
-                      const SequenceToken& sequence_token) override;
+                      internal::Sequence* sequence) override;
 
   // Synchronizes accesses to members below.
   Lock lock_;
@@ -205,7 +205,7 @@
 
 void ScopedTaskEnvironment::TestTaskTracker::PerformRunTask(
     std::unique_ptr<internal::Task> task,
-    const SequenceToken& sequence_token) {
+    internal::Sequence* sequence) {
   {
     AutoLock auto_lock(lock_);
 
@@ -216,7 +216,7 @@
   }
 
   internal::TaskSchedulerImpl::TaskTrackerImpl::PerformRunTask(std::move(task),
-                                                               sequence_token);
+                                                               sequence);
 
   {
     AutoLock auto_lock(lock_);
diff --git a/base/test/scoped_task_scheduler.cc b/base/test/scoped_task_scheduler.cc
index b31d4bd..9a97edd7 100644
--- a/base/test/scoped_task_scheduler.cc
+++ b/base/test/scoped_task_scheduler.cc
@@ -20,6 +20,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_runner.h"
+#include "base/task_scheduler/sequence.h"
 #include "base/task_scheduler/single_thread_task_runner_thread_mode.h"
 #include "base/task_scheduler/task.h"
 #include "base/task_scheduler/task_scheduler.h"
@@ -75,14 +76,14 @@
   void FlushForTesting() override;
   void JoinForTesting() override;
 
-  // Posts |task| to this TaskScheduler with |sequence_token|. Returns true on
-  // success.
+  // Adds |task| into |sequence| and posts a task to run the next task in
+  // |sequence| to the MessageLoop backing this TaskScheduler.
+  // Returns true on success.
   bool PostTask(std::unique_ptr<internal::Task> task,
-                const SequenceToken& sequence_token);
+                scoped_refptr<internal::Sequence> sequence);
 
-  // Runs |task| with |sequence_token| using this TaskScheduler's TaskTracker.
-  void RunTask(std::unique_ptr<internal::Task> task,
-               const SequenceToken& sequence_token);
+  // Runs the next task from |sequence| using this TaskScheduler's TaskTracker.
+  void RunNextTask(scoped_refptr<internal::Sequence> sequence);
 
   // Returns true if this TaskScheduler runs its tasks on the current thread.
   bool RunsTasksInCurrentSequence() const;
@@ -153,7 +154,7 @@
 
   TestTaskScheduler* const task_scheduler_;
   const ExecutionMode execution_mode_;
-  const SequenceToken sequence_token_;
+  const scoped_refptr<internal::Sequence> sequence_;
   const TaskTraits traits_;
 
   DISALLOW_COPY_AND_ASSIGN(TestTaskSchedulerTaskRunner);
@@ -244,20 +245,27 @@
 }
 
 bool TestTaskScheduler::PostTask(std::unique_ptr<internal::Task> task,
-                                 const SequenceToken& sequence_token) {
+                                 scoped_refptr<internal::Sequence> sequence) {
   DCHECK(task);
   if (!task_tracker_.WillPostTask(task.get()))
     return false;
   internal::Task* const task_ptr = task.get();
+
+  // Create a one-off single-task Sequence if no Sequence is provided by the
+  // caller.
+  if (!sequence)
+    sequence = MakeRefCounted<internal::Sequence>();
+
+  sequence->PushTask(std::move(task));
+
   return MessageLoopTaskRunner()->PostDelayedTask(
       task_ptr->posted_from,
-      BindOnce(&TestTaskScheduler::RunTask, Unretained(this), Passed(&task),
-               sequence_token),
+      BindOnce(&TestTaskScheduler::RunNextTask, Unretained(this), sequence),
       task_ptr->delay);
 }
 
-void TestTaskScheduler::RunTask(std::unique_ptr<internal::Task> task,
-                                const SequenceToken& sequence_token) {
+void TestTaskScheduler::RunNextTask(
+    scoped_refptr<internal::Sequence> sequence) {
   DCHECK(!saved_task_runner_);
   saved_task_runner_ = MessageLoop::current()->task_runner();
 
@@ -266,9 +274,7 @@
   MessageLoop::current()->ClearTaskRunnerForTesting();
 
   // Run the task.
-  task_tracker_.RunTask(std::move(task), sequence_token.IsValid()
-                                             ? sequence_token
-                                             : SequenceToken::Create());
+  task_tracker_.RunNextTask(sequence.get());
 
   // Make sure that any task runner that was registered was also cleaned up.
   DCHECK(!MessageLoop::current()->task_runner());
@@ -288,9 +294,9 @@
     TaskTraits traits)
     : task_scheduler_(task_scheduler),
       execution_mode_(execution_mode),
-      sequence_token_(execution_mode == ExecutionMode::PARALLEL
-                          ? SequenceToken()
-                          : SequenceToken::Create()),
+      sequence_(execution_mode == ExecutionMode::PARALLEL
+                    ? nullptr
+                    : MakeRefCounted<internal::Sequence>()),
       traits_(traits) {}
 
 bool TestTaskSchedulerTaskRunner::PostDelayedTask(
@@ -303,7 +309,8 @@
     task->sequenced_task_runner_ref = make_scoped_refptr(this);
   else if (execution_mode_ == ExecutionMode::SINGLE_THREADED)
     task->single_thread_task_runner_ref = make_scoped_refptr(this);
-  return task_scheduler_->PostTask(std::move(task), sequence_token_);
+
+  return task_scheduler_->PostTask(std::move(task), sequence_);
 }
 
 bool TestTaskSchedulerTaskRunner::PostNonNestableDelayedTask(
@@ -317,7 +324,7 @@
 bool TestTaskSchedulerTaskRunner::RunsTasksInCurrentSequence() const {
   if (execution_mode_ == ExecutionMode::PARALLEL)
     return task_scheduler_->RunsTasksInCurrentSequence();
-  return sequence_token_ == SequenceToken::GetForCurrentThread();
+  return sequence_->token() == SequenceToken::GetForCurrentThread();
 }
 
 TestTaskSchedulerTaskRunner::~TestTaskSchedulerTaskRunner() = default;
diff --git a/base/threading/non_thread_safe.h b/base/threading/non_thread_safe.h
deleted file mode 100644
index 407b2e1..0000000
--- a/base/threading/non_thread_safe.h
+++ /dev/null
@@ -1,45 +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 BASE_THREADING_NON_THREAD_SAFE_H_
-#define BASE_THREADING_NON_THREAD_SAFE_H_
-
-// Classes deriving from NonThreadSafe may need to suppress MSVC warning 4275:
-// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
-// There is a specific macro to do it: NON_EXPORTED_BASE(), defined in
-// compiler_specific.h
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "base/threading/non_thread_safe_impl.h"
-
-namespace base {
-
-// Do nothing implementation of NonThreadSafe, for release mode.
-//
-// Note: You should almost always use the NonThreadSafe class to get
-// the right version of the class for your build configuration.
-class NonThreadSafeDoNothing {
- public:
-  bool CalledOnValidThread() const {
-    return true;
-  }
-
- protected:
-  ~NonThreadSafeDoNothing() {}
-  void DetachFromThread() {}
-};
-
-// DEPRECATED! Use base::SequenceChecker (for thread-safety) or
-// base::ThreadChecker (for thread-affinity) -- see their documentation for
-// details. Use a checker as a protected member instead of inheriting from
-// NonThreadSafe if you need subclasses to have access.
-#if DCHECK_IS_ON()
-typedef NonThreadSafeImpl NonThreadSafe;
-#else
-typedef NonThreadSafeDoNothing NonThreadSafe;
-#endif  // DCHECK_IS_ON()
-
-}  // namespace base
-
-#endif  // BASE_THREADING_NON_THREAD_SAFE_H_
diff --git a/base/threading/non_thread_safe_impl.cc b/base/threading/non_thread_safe_impl.cc
deleted file mode 100644
index 7e729d9..0000000
--- a/base/threading/non_thread_safe_impl.cc
+++ /dev/null
@@ -1,23 +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 "base/threading/non_thread_safe_impl.h"
-
-#include "base/logging.h"
-
-namespace base {
-
-bool NonThreadSafeImpl::CalledOnValidThread() const {
-  return thread_checker_.CalledOnValidThread();
-}
-
-NonThreadSafeImpl::~NonThreadSafeImpl() {
-  DCHECK(CalledOnValidThread());
-}
-
-void NonThreadSafeImpl::DetachFromThread() {
-  thread_checker_.DetachFromThread();
-}
-
-}  // namespace base
diff --git a/base/threading/non_thread_safe_impl.h b/base/threading/non_thread_safe_impl.h
deleted file mode 100644
index a3a356df..0000000
--- a/base/threading/non_thread_safe_impl.h
+++ /dev/null
@@ -1,39 +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 BASE_THREADING_NON_THREAD_SAFE_IMPL_H_
-#define BASE_THREADING_NON_THREAD_SAFE_IMPL_H_
-
-#include "base/base_export.h"
-#include "base/threading/thread_checker_impl.h"
-
-namespace base {
-
-// Full implementation of NonThreadSafe, for debug mode or for occasional
-// temporary use in release mode e.g. when you need to CHECK on a thread
-// bug that only occurs in the wild.
-//
-// Note: You should almost always use the NonThreadSafe class to get
-// the right version of the class for your build configuration.
-class BASE_EXPORT NonThreadSafeImpl {
- public:
-  bool CalledOnValidThread() const;
-
- protected:
-  ~NonThreadSafeImpl();
-
-  // Changes the thread that is checked for in CalledOnValidThread. The next
-  // call to CalledOnValidThread will attach this class to a new thread. It is
-  // up to the NonThreadSafe derived class to decide to expose this or not.
-  // This may be useful when an object may be created on one thread and then
-  // used exclusively on another thread.
-  void DetachFromThread();
-
- private:
-  ThreadCheckerImpl thread_checker_;
-};
-
-}  // namespace base
-
-#endif  // BASE_THREADING_NON_THREAD_SAFE_IMPL_H_
diff --git a/base/threading/non_thread_safe_unittest.cc b/base/threading/non_thread_safe_unittest.cc
deleted file mode 100644
index 89d48a09..0000000
--- a/base/threading/non_thread_safe_unittest.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/threading/non_thread_safe.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/test/gtest_util.h"
-#include "base/threading/simple_thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-namespace {
-
-// Simple class to exersice the basics of NonThreadSafe.
-// Both the destructor and DoStuff should verify that they were
-// called on the same thread as the constructor.
-class NonThreadSafeClass : public NonThreadSafe {
- public:
-  NonThreadSafeClass() {}
-
-  // Verifies that it was called on the same thread as the constructor.
-  void DoStuff() {
-    DCHECK(CalledOnValidThread());
-  }
-
-  void DetachFromThread() {
-    NonThreadSafe::DetachFromThread();
-  }
-
-  static void MethodOnDifferentThreadImpl();
-  static void DestructorOnDifferentThreadImpl();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NonThreadSafeClass);
-};
-
-// Calls NonThreadSafeClass::DoStuff on another thread.
-class CallDoStuffOnThread : public SimpleThread {
- public:
-  explicit CallDoStuffOnThread(NonThreadSafeClass* non_thread_safe_class)
-      : SimpleThread("call_do_stuff_on_thread"),
-        non_thread_safe_class_(non_thread_safe_class) {
-  }
-
-  void Run() override { non_thread_safe_class_->DoStuff(); }
-
- private:
-  NonThreadSafeClass* non_thread_safe_class_;
-
-  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
-};
-
-// Deletes NonThreadSafeClass on a different thread.
-class DeleteNonThreadSafeClassOnThread : public SimpleThread {
- public:
-  explicit DeleteNonThreadSafeClassOnThread(
-      NonThreadSafeClass* non_thread_safe_class)
-      : SimpleThread("delete_non_thread_safe_class_on_thread"),
-        non_thread_safe_class_(non_thread_safe_class) {
-  }
-
-  void Run() override { non_thread_safe_class_.reset(); }
-
- private:
-  std::unique_ptr<NonThreadSafeClass> non_thread_safe_class_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeleteNonThreadSafeClassOnThread);
-};
-
-}  // namespace
-
-TEST(NonThreadSafeTest, CallsAllowedOnSameThread) {
-  std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
-      new NonThreadSafeClass);
-
-  // Verify that DoStuff doesn't assert.
-  non_thread_safe_class->DoStuff();
-
-  // Verify that the destructor doesn't assert.
-  non_thread_safe_class.reset();
-}
-
-TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) {
-  std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
-      new NonThreadSafeClass);
-
-  // Verify that the destructor doesn't assert when called on a different thread
-  // after a detach.
-  non_thread_safe_class->DetachFromThread();
-  DeleteNonThreadSafeClassOnThread delete_on_thread(
-      non_thread_safe_class.release());
-
-  delete_on_thread.Start();
-  delete_on_thread.Join();
-}
-
-void NonThreadSafeClass::MethodOnDifferentThreadImpl() {
-  std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
-      new NonThreadSafeClass);
-
-  // Verify that DoStuff asserts in debug builds only when called
-  // on a different thread.
-  CallDoStuffOnThread call_on_thread(non_thread_safe_class.get());
-
-  call_on_thread.Start();
-  call_on_thread.Join();
-}
-
-#if DCHECK_IS_ON()
-TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  ASSERT_DCHECK_DEATH({ NonThreadSafeClass::MethodOnDifferentThreadImpl(); });
-}
-#else
-TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) {
-  NonThreadSafeClass::MethodOnDifferentThreadImpl();
-}
-#endif  // DCHECK_IS_ON()
-
-void NonThreadSafeClass::DestructorOnDifferentThreadImpl() {
-  std::unique_ptr<NonThreadSafeClass> non_thread_safe_class(
-      new NonThreadSafeClass);
-
-  // Verify that the destructor asserts in debug builds only
-  // when called on a different thread.
-  DeleteNonThreadSafeClassOnThread delete_on_thread(
-      non_thread_safe_class.release());
-
-  delete_on_thread.Start();
-  delete_on_thread.Join();
-}
-
-#if DCHECK_IS_ON()
-TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) {
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  ASSERT_DCHECK_DEATH(
-      { NonThreadSafeClass::DestructorOnDifferentThreadImpl(); });
-}
-#else
-TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) {
-  NonThreadSafeClass::DestructorOnDifferentThreadImpl();
-}
-#endif  // DCHECK_IS_ON()
-
-}  // namespace base
diff --git a/base/threading/thread_checker_impl.h b/base/threading/thread_checker_impl.h
index 13193d12..103dfe7 100644
--- a/base/threading/thread_checker_impl.h
+++ b/base/threading/thread_checker_impl.h
@@ -45,9 +45,9 @@
   // TaskToken for which CalledOnValidThread() always returns true. This allows
   // CalledOnValidThread() to return true when called multiple times from the
   // same task, even if it's not running in a single-threaded context itself
-  // (allowing usage of ThreadChecker/NonThreadSafe objects on the stack in the
-  // scope of one-off tasks). Note: CalledOnValidThread() may return true even
-  // if the current TaskToken is not equal to this.
+  // (allowing usage of ThreadChecker objects on the stack in the scope of one-
+  // off tasks). Note: CalledOnValidThread() may return true even if the current
+  // TaskToken is not equal to this.
   mutable TaskToken task_token_;
 
   // SequenceToken for which CalledOnValidThread() may return true. Used to
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
index 8dd7743..b6563d58 100644
--- a/base/threading/thread_restrictions.cc
+++ b/base/threading/thread_restrictions.cc
@@ -35,12 +35,13 @@
 // static
 void ThreadRestrictions::AssertIOAllowed() {
   if (g_io_disallowed.Get().Get()) {
-    NOTREACHED() <<
-        "Function marked as IO-only was called from a thread that "
-        "disallows IO!  If this thread really should be allowed to "
-        "make IO calls, adjust the call to "
-        "base::ThreadRestrictions::SetIOAllowed() in this thread's "
-        "startup.";
+    NOTREACHED() << "Function marked as IO-only was called from a thread that "
+                    "disallows IO!  If this thread really should be allowed to "
+                    "make IO calls, adjust the call to "
+                    "base::ThreadRestrictions::SetIOAllowed() in this thread's "
+                    "startup.  If this task is running inside the "
+                    "TaskScheduler, the TaskRunner used to post it needs to "
+                    "have MayBlock() in its TaskTraits.";
   }
 }
 
@@ -74,7 +75,9 @@
 void ThreadRestrictions::AssertWaitAllowed() {
   if (g_wait_disallowed.Get().Get()) {
     NOTREACHED() << "Waiting is not allowed to be used on this thread to "
-                 << "prevent jank and deadlock.";
+                 << "prevent jank and deadlock.  If this task is running "
+                    "inside the TaskScheduler, the TaskRunner used to post it "
+                    "needs to have WithBaseSyncPrimitives() in its TaskTraits.";
   }
 }
 
diff --git a/build/OWNERS b/build/OWNERS
index c644ca7d..991d98d 100644
--- a/build/OWNERS
+++ b/build/OWNERS
@@ -7,6 +7,7 @@
 brucedawson@chromium.org
 
 per-file .gitignore=*
+per-file check_gn_headers_whitelist.txt=*
 per-file mac_toolchain.py=erikchen@chromium.org
 per-file mac_toolchain.py=justincohen@chromium.org
 per-file package_mac_toolchain.py=erikchen@chromium.org
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
index d2a8282c..f9c20418 100644
--- a/build/check_gn_headers_whitelist.txt
+++ b/build/check_gn_headers_whitelist.txt
@@ -1,3 +1,6 @@
+# Do not add files to this whitelist unless you are adding a new OS or
+# changing the GN arguments on bots.
+
 ash/accelerators/accelerator_controller_delegate.h
 ash/accelerators/accelerator_controller_delegate_aura.h
 ash/accelerators/accelerator_table.h
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index ecda2b9..cf617028 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -126,191 +126,219 @@
     }
   }
 
-  executable("chrome_initial") {
+  template("chrome_binary") {
+    executable(target_name) {
+      output_name = invoker.output_name
+      sources = invoker.sources
+      if (defined(invoker.deps)) {
+        deps = invoker.deps
+      } else {
+        deps = []
+      }
+
+      if (!is_win || is_clang) {
+        # Normally, we need to pass specific flags to the linker to
+        # create an executable that gathers profile data. However, when
+        # using MSVC, we need to make sure we *don't* pass /GENPROFILE
+        # when linking without generating any code, or else the linker
+        # will give us fatal error LNK1264. So we add the PGO flags
+        # on all configurations, execpt MSVC on Windows.
+        configs += [ "//build/config/compiler/pgo:default_pgo_flags" ]
+      }
+
+      # Because the sources list varies so significantly per-platform, generally
+      # each platform lists its own files rather than relying on filtering or
+      # removing unused files.
+      sources += [ "app/chrome_exe_resource.h" ]
+      defines = []
+      public_deps = []
+      deps += [
+        "//build/config:exe_and_shlib_deps",
+        "//printing/features",
+      ]
+
+      data = [
+        "$root_out_dir/resources.pak",
+      ]
+      if (is_linux || is_win) {
+        data += [
+          "$root_out_dir/chrome_100_percent.pak",
+          "$root_out_dir/locales/en-US.pak",
+          "$root_out_dir/locales/fr.pak",
+        ]
+      }
+
+      data_deps = []
+
+      if (is_win) {
+        sources += [
+          "app/chrome_exe.rc",
+          "app/chrome_exe_load_config_win.cc",
+          "app/chrome_exe_main_win.cc",
+          "app/chrome_watcher_client_win.cc",
+          "app/chrome_watcher_client_win.h",
+          "app/chrome_watcher_command_line_win.cc",
+          "app/chrome_watcher_command_line_win.h",
+          "app/main_dll_loader_win.cc",
+          "app/main_dll_loader_win.h",
+          "common/crash_keys.cc",
+          "common/crash_keys.h",
+        ]
+
+        deps += [
+          ":chrome_dll",
+          ":chrome_exe_version",
+          ":copy_first_run",
+          ":file_pre_reader",
+          ":visual_elements_resources",
+          "//base",
+          "//breakpad:breakpad_handler",
+          "//breakpad:breakpad_sender",
+          "//chrome/app/version_assembly:chrome_exe_manifest",
+          "//chrome/browser:chrome_process_finder",
+          "//chrome/chrome_watcher",
+          "//chrome/chrome_watcher:client",
+          "//chrome/common:constants",
+          "//chrome/common:metrics_constants_util_win",
+          "//chrome/install_static:secondary_module",
+          "//chrome/installer/util:with_no_strings",
+          "//chrome_elf",
+          "//components/browser_watcher:browser_watcher_client",
+          "//components/crash/content/app:run_as_crashpad_handler",
+          "//components/flags_ui:switches",
+          "//content:sandbox_helper_win",
+          "//content/public/common:static_switches",
+          "//crypto",
+          "//gpu/config:crash_keys",
+          "//sandbox",
+        ]
+        data_deps = [
+          "//chrome/app/version_assembly:version_assembly_manifest",
+        ]
+
+        if (win_console_app) {
+          defines += [ "WIN_CONSOLE_APP" ]
+        } else {
+          # Set /SUBSYSTEM:WINDOWS for chrome.exe itself, unless a console build
+          # has been requested.
+          configs -= [ "//build/config/win:console" ]
+          configs += [ "//build/config/win:windowed" ]
+        }
+
+        ldflags = [
+          "/DELAYLOAD:dbghelp.dll",
+          "/DELAYLOAD:dwmapi.dll",
+          "/DELAYLOAD:uxtheme.dll",
+          "/DELAYLOAD:ole32.dll",
+          "/DELAYLOAD:oleaut32.dll",
+        ]
+
+        if (current_cpu == "x64") {
+          # Increase the initial stack size. The default is 1MB, this is 2MB.
+          ldflags += [ "/STACK:2097152" ]
+        }
+      }
+
+      if (is_linux) {
+        sources += [
+          "app/chrome_dll_resource.h",
+          "app/chrome_main.cc",
+          "app/chrome_main_delegate.cc",
+          "app/chrome_main_delegate.h",
+        ]
+
+        deps += [
+          # On Linux, link the dependencies (libraries) that make up actual
+          # Chromium functionality directly into the executable.
+          ":browser_dependencies",
+          ":child_dependencies",
+          ":manpage",
+
+          # Needed to use the master_preferences functions
+          "//chrome/installer/util:with_no_strings",
+          "//content/public/app:both",
+          "//content/public/common:service_names",
+
+          # For headless mode.
+          "//headless:headless_shell_lib",
+          "//services/service_manager/embedder",
+        ]
+
+        public_deps = [
+          ":xdg_mime",  # Needs to be public for installer to consume files.
+          "//chrome/common:features",
+        ]
+
+        ldflags = [ "-pie" ]
+
+        # Chrome OS debug builds for arm need to pass --long-plt to the linker.
+        # See https://bugs.chromium.org/p/chromium/issues/detail?id=583532
+        if (is_chromeos && is_debug && target_cpu == "arm") {
+          ldflags += [ "-Wl,--long-plt" ]
+        }
+
+        if (use_pango || use_cairo) {
+          # Needed for chrome_main.cc initialization of libraries.
+          configs += [ "//build/config/linux/pangocairo" ]
+        }
+
+        if (use_x11) {
+          configs += [
+            "//build/config/linux:x11",
+            "//build/config/linux:xext",
+          ]
+        }
+
+        if (enable_package_mash_services) {
+          deps += embedded_mash_service_deps
+        }
+      }
+
+      # These files are used by the installer so we need a public dep.
+      public_deps += [ ":packed_resources" ]
+
+      # Only ChromeOS has precompiled Flash that needs to get copied to the output
+      # directory. On other platforms, Flash is either component-updated only or
+      # not supported at all.
+      if (is_chromeos) {
+        deps += [ "//third_party/adobe/flash:flapper_binaries" ]
+      }
+
+      data_deps += [ "//third_party/widevine/cdm:widevinecdmadapter" ]
+
+      if (is_multi_dll_chrome) {
+        defines += [ "CHROME_MULTIPLE_DLL" ]
+        data_deps += [ ":chrome_child" ]
+      }
+    }
+  }
+
+  chrome_binary("chrome_initial") {
     if (is_win) {
       output_name = "initialexe/chrome"
     } else {
       output_name = "chrome"
     }
 
-    if (!is_win || is_clang) {
-      # Normally, we need to pass specific flags to the linker to
-      # create an executable that gathers profile data. However, when
-      # using MSVC, we need to make sure we *don't* pass /GENPROFILE
-      # when linking without generating any code, or else the linker
-      # will give us fatal error LNK1264. So we add the PGO flags
-      # on all configurations, execpt MSVC on Windows.
-      configs += [ "//build/config/compiler/pgo:default_pgo_flags" ]
-    }
-
-    # Because the sources list varies so significantly per-platform, generally
-    # each platform lists its own files rather than relying on filtering or
-    # removing unused files.
-    sources = [
-      "app/chrome_exe_resource.h",
-    ]
-    defines = []
-    public_deps = []
-    deps = [
-      "//build/config:exe_and_shlib_deps",
-      "//printing/features",
-    ]
-
-    data = [
-      "$root_out_dir/resources.pak",
-    ]
-    if (is_linux || is_win) {
-      data += [
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/locales/en-US.pak",
-        "$root_out_dir/locales/fr.pak",
-      ]
-    }
-
-    data_deps = []
-
-    if (is_win) {
-      sources += [
-        "app/chrome_exe.rc",
-        "app/chrome_exe_load_config_win.cc",
-        "app/chrome_exe_main_win.cc",
-        "app/chrome_watcher_client_win.cc",
-        "app/chrome_watcher_client_win.h",
-        "app/chrome_watcher_command_line_win.cc",
-        "app/chrome_watcher_command_line_win.h",
-        "app/main_dll_loader_win.cc",
-        "app/main_dll_loader_win.h",
-        "common/crash_keys.cc",
-        "common/crash_keys.h",
-      ]
-
-      deps += [
-        ":chrome_dll",
-        ":chrome_exe_version",
-        ":copy_first_run",
-        ":file_pre_reader",
-        ":visual_elements_resources",
-        "//base",
-        "//breakpad:breakpad_handler",
-        "//breakpad:breakpad_sender",
-        "//chrome/app/version_assembly:chrome_exe_manifest",
-        "//chrome/browser:chrome_process_finder",
-        "//chrome/chrome_watcher",
-        "//chrome/chrome_watcher:client",
-        "//chrome/common:constants",
-        "//chrome/common:metrics_constants_util_win",
-        "//chrome/install_static:secondary_module",
-        "//chrome/installer/util:with_no_strings",
-        "//chrome_elf",
-        "//components/browser_watcher:browser_watcher_client",
-        "//components/crash/content/app:run_as_crashpad_handler",
-        "//components/flags_ui:switches",
-        "//content:sandbox_helper_win",
-        "//content/public/common:static_switches",
-        "//crypto",
-        "//gpu/config:crash_keys",
-        "//sandbox",
-      ]
-      data_deps = [
-        "//chrome/app/version_assembly:version_assembly_manifest",
-      ]
-
-      if (win_console_app) {
-        defines += [ "WIN_CONSOLE_APP" ]
-      } else {
-        # Set /SUBSYSTEM:WINDOWS for chrome.exe itself, unless a console build
-        # has been requested.
-        configs -= [ "//build/config/win:console" ]
-        configs += [ "//build/config/win:windowed" ]
-      }
-
-      ldflags = [
-        "/DELAYLOAD:dbghelp.dll",
-        "/DELAYLOAD:dwmapi.dll",
-        "/DELAYLOAD:uxtheme.dll",
-        "/DELAYLOAD:ole32.dll",
-        "/DELAYLOAD:oleaut32.dll",
-      ]
-
-      if (current_cpu == "x64") {
-        # Increase the initial stack size. The default is 1MB, this is 2MB.
-        ldflags += [ "/STACK:2097152" ]
-      }
-    } else if (use_aura) {
+    sources = []
+    if (!is_win && use_aura) {
       # Non-Windows aura entrypoint.
       sources += [ "app/chrome_exe_main_aura.cc" ]
     }
+  }
 
-    if (is_linux) {
-      sources += [
-        "app/chrome_dll_resource.h",
-        "app/chrome_main.cc",
-        "app/chrome_main_delegate.cc",
-        "app/chrome_main_delegate.h",
+  if (enable_package_mash_services) {
+    chrome_binary("chrome_test") {
+      # Note that the output name needs to end with "chrome", because that's what
+      # telemetry test-runner expects.
+      output_name = "test_chrome"
+      sources = []
+      deps = [
+        "//chrome/app/mash:chrome_test_catalog",
       ]
-
-      deps += [
-        # On Linux, link the dependencies (libraries) that make up actual
-        # Chromium functionality directly into the executable.
-        ":browser_dependencies",
-        ":child_dependencies",
-        ":manpage",
-
-        # Needed to use the master_preferences functions
-        "//chrome/installer/util:with_no_strings",
-        "//content/public/app:both",
-        "//content/public/common:service_names",
-
-        # For headless mode.
-        "//headless:headless_shell_lib",
-        "//services/service_manager/embedder",
-      ]
-
-      public_deps = [
-        ":xdg_mime",  # Needs to be public for installer to consume files.
-        "//chrome/common:features",
-      ]
-
-      ldflags = [ "-pie" ]
-
-      # Chrome OS debug builds for arm need to pass --long-plt to the linker.
-      # See https://bugs.chromium.org/p/chromium/issues/detail?id=583532
-      if (is_chromeos && is_debug && target_cpu == "arm") {
-        ldflags += [ "-Wl,--long-plt" ]
+      if (!is_win && use_aura) {
+        sources += [ "app/chrome_test_exe_main_aura.cc" ]
       }
-
-      if (use_pango || use_cairo) {
-        # Needed for chrome_main.cc initialization of libraries.
-        configs += [ "//build/config/linux/pangocairo" ]
-      }
-
-      if (use_x11) {
-        configs += [
-          "//build/config/linux:x11",
-          "//build/config/linux:xext",
-        ]
-      }
-
-      if (enable_package_mash_services) {
-        deps += embedded_mash_service_deps
-      }
-    }
-
-    # These files are used by the installer so we need a public dep.
-    public_deps += [ ":packed_resources" ]
-
-    # Only ChromeOS has precompiled Flash that needs to get copied to the output
-    # directory. On other platforms, Flash is either component-updated only or
-    # not supported at all.
-    if (is_chromeos) {
-      deps += [ "//third_party/adobe/flash:flapper_binaries" ]
-    }
-
-    data_deps += [ "//third_party/widevine/cdm:widevinecdmadapter" ]
-
-    if (is_multi_dll_chrome) {
-      defines += [ "CHROME_MULTIPLE_DLL" ]
-      data_deps += [ ":chrome_child" ]
     }
   }
 }  # !is_android && !is_mac
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index ad90940e..8314f7c 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -282,6 +282,7 @@
     java_files += chrome_vr_java_sources
     deps += [ "//third_party/gvr-android-sdk:gvr_common_java" ]
   }
+  srcjar_deps += [ ":chrome_vr_android_java_enums_srcjar" ]
 
   # Add the actual implementation where necessary so that downstream targets
   # can provide their own implementations.
@@ -318,6 +319,12 @@
   ]
 }
 
+java_cpp_enum("chrome_vr_android_java_enums_srcjar") {
+  sources = [
+    "//chrome/browser/android/vr_shell/vr_core_info.h",
+  ]
+}
+
 proto_java_library("document_tab_model_info_proto_java") {
   proto_path = "java/src/org/chromium/chrome/browser/tabmodel/document"
   sources = [
diff --git a/chrome/android/java/res/layout/history_item_view.xml b/chrome/android/java/res/layout/history_item_view.xml
index a01eb443..1cda7a8 100644
--- a/chrome/android/java/res/layout/history_item_view.xml
+++ b/chrome/android/java/res/layout/history_item_view.xml
@@ -52,7 +52,7 @@
               android:layout_height="wrap_content"
               android:singleLine="true"
               android:ellipsize="end"
-              android:textColor="@color/google_grey_600"
+              android:textColor="@color/black_alpha_54"
               android:textSize="14sp" />
         </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/toolbar_phone_common.xml b/chrome/android/java/res/layout/toolbar_phone_common.xml
index 4d6091e..36bc4011 100644
--- a/chrome/android/java/res/layout/toolbar_phone_common.xml
+++ b/chrome/android/java/res/layout/toolbar_phone_common.xml
@@ -26,13 +26,6 @@
         android:contentDescription="@string/accessibility_toolbar_btn_home"
         android:visibility="gone" />
 
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/expand_sheet_button"
-        style="@style/ToolbarButton"
-        android:src="@drawable/ic_collapsed"
-        android:contentDescription="@string/accessibility_toolbar_btn_expand"
-        android:visibility="gone" />
-
     <org.chromium.chrome.browser.omnibox.LocationBarPhone
         android:id="@+id/location_bar"
         android:layout_width="match_parent"
@@ -49,6 +42,13 @@
             android:layout_width="4dp"
             android:layout_height="match_parent" />
 
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/expand_sheet_button"
+            style="@style/ToolbarButton"
+            android:src="@drawable/ic_collapsed"
+            android:contentDescription="@string/accessibility_toolbar_btn_expand"
+            android:visibility="gone" />
+
         <ImageButton android:id="@+id/tab_switcher_button"
             style="@style/ToolbarButton"
             android:layout_gravity="top"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 4f24ef9..040de596 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -635,6 +635,7 @@
         <item name="android:layout_marginTop">12dp</item>
         <item name="android:layout_marginBottom">10dp</item>
         <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@color/black_alpha_54</item>
     </style>
     <style name="DownloadRowContainer">
         <item name="android:layout_width">match_parent</item>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 9bac47030..03a1ce0 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -5,7 +5,7 @@
 
 <resources>
     <!-- Common colors -->
-    <color name="default_text_color">#333</color>
+    <color name="default_text_color">@color/black_alpha_87</color>
     <color name="default_primary_color">#f2f2f2</color>
     <color name="light_normal_color">#5A5A5A</color>
     <color name="light_active_color">#4285F4</color>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
index 768d0c6..92c2176f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -63,7 +63,7 @@
     private static final String DISABLE_AMP_AS_SEPARATE_TAB = "disable_amp_as_separate_tab";
 
     // Machine Learning
-    private static final String ENABLE_RANKER_LOGGING = "enable_ranker_logging";
+    private static final String DISABLE_RANKER_LOGGING = "disable_ranker_logging";
 
     // Privacy-related flags
     private static final String DISABLE_SEND_HOME_COUNTRY = "disable_send_home_country";
@@ -93,7 +93,7 @@
     private static Boolean sIsSendHomeCountryDisabled;
     private static Boolean sIsPageContentNotificationDisabled;
     private static Boolean sContextualSearchUrlActionsEnabled;
-    private static Boolean sIsRankerLoggingEnabled;
+    private static Boolean sIsRankerLoggingDisabled;
     private static Integer sWaitAfterTapDelayMs;
 
     /**
@@ -338,14 +338,14 @@
     }
 
     /**
-     * @return Whether or not logging to Ranker is enabled.
+     * @return Whether or not logging to Ranker is disabled.
      */
-    static boolean isRankerLoggingEnabled() {
-        if (sIsRankerLoggingEnabled == null) {
-            sIsRankerLoggingEnabled = getBooleanParam(ENABLE_RANKER_LOGGING);
+    static boolean isRankerLoggingDisabled() {
+        if (sIsRankerLoggingDisabled == null) {
+            sIsRankerLoggingDisabled = getBooleanParam(DISABLE_RANKER_LOGGING);
         }
 
-        return sIsRankerLoggingEnabled;
+        return sIsRankerLoggingDisabled;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
index 3948f0e6..8fe0f7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -151,7 +151,7 @@
 
     /** Whether actually writing data is enabled.  If not, we may do nothing, or just print. */
     private boolean isEnabled() {
-        return ContextualSearchFieldTrial.isRankerLoggingEnabled();
+        return !ContextualSearchFieldTrial.isRankerLoggingDisabled();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
index 73dce632..67a74465 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -326,16 +326,6 @@
     }
 
     @Override
-    protected int getBoundsAfterAccountingForLeftButton() {
-        int padding = super.getBoundsAfterAccountingForLeftButton();
-        if (!mUseToolbarHandle && mExpandButton.getVisibility() != GONE
-                && !mShouldHideToolbarButtons) {
-            padding = mExpandButton.getMeasuredWidth();
-        }
-        return padding;
-    }
-
-    @Override
     protected int getLeftPositionOfLocationBarBackground(VisualState visualState) {
         if (!mAnimatingToolbarButtonAppearance && !mAnimatingToolbarButtonDisappearance) {
             mLocationBarBackgroundLeftPosition =
@@ -366,19 +356,15 @@
     }
 
     private int getLocationBarBackgroundLeftOffset() {
-        if (!ApiCompatibilityUtils.isLayoutRtl(this)) {
-            return !mUseToolbarHandle ? mExpandButton.getMeasuredWidth() - mToolbarSidePadding : 0;
-        } else {
-            return mToolbarButtonsContainer.getMeasuredWidth() - mToolbarSidePadding;
-        }
+        return !ApiCompatibilityUtils.isLayoutRtl(this)
+                ? 0
+                : mToolbarButtonsContainer.getMeasuredWidth() - mToolbarSidePadding;
     }
 
     private int getLocationBarBackgroundRightOffset() {
-        if (!ApiCompatibilityUtils.isLayoutRtl(this)) {
-            return mToolbarButtonsContainer.getMeasuredWidth() - mToolbarSidePadding;
-        } else {
-            return !mUseToolbarHandle ? mExpandButton.getMeasuredWidth() - mToolbarSidePadding : 0;
-        }
+        return !ApiCompatibilityUtils.isLayoutRtl(this)
+                ? mToolbarButtonsContainer.getMeasuredWidth() - mToolbarSidePadding
+                : 0;
     }
 
     @Override
@@ -391,15 +377,6 @@
     }
 
     @Override
-    protected void updateUrlExpansionAnimation() {
-        super.updateUrlExpansionAnimation();
-
-        if (!mUseToolbarHandle) {
-            mExpandButton.setVisibility(mShouldHideToolbarButtons ? View.INVISIBLE : View.VISIBLE);
-        }
-    }
-
-    @Override
     protected boolean isChildLeft(View child) {
         return (child == mNewTabButton || child == mExpandButton) ^ LocalizationUtils.isLayoutRtl();
     }
@@ -643,12 +620,6 @@
                 mToolbarButtonsContainer.setVisibility(View.VISIBLE);
                 mToolbarButtonsContainer.setTranslationX(0);
 
-                if (!mUseToolbarHandle) {
-                    mExpandButton.setAlpha(1.f);
-                    mExpandButton.setVisibility(View.VISIBLE);
-                    mExpandButton.setTranslationX(0);
-                }
-
                 requestLayout();
             } else {
                 mToolbarButtonVisibilityPercent = 0.f;
@@ -728,18 +699,6 @@
         mToolbarButtonsContainer.setVisibility(
                 mToolbarButtonVisibilityPercent > 0.f ? View.VISIBLE : View.INVISIBLE);
 
-        if (!mUseToolbarHandle) {
-            float expandButtonWidth = mExpandButton.getMeasuredWidth();
-            float expandButtonTranslationX =
-                    expandButtonWidth * (1.f - mToolbarButtonVisibilityPercent);
-            if (!isRtl) expandButtonTranslationX *= -1;
-
-            mExpandButton.setTranslationX(expandButtonTranslationX);
-            mExpandButton.setAlpha(mToolbarButtonVisibilityPercent);
-            mExpandButton.setVisibility(
-                    mToolbarButtonVisibilityPercent > 0.f ? View.VISIBLE : View.INVISIBLE);
-        }
-
         float locationBarTranslationX;
         boolean isLocationBarRtl = ApiCompatibilityUtils.isLayoutRtl(mLocationBar);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index cd3051c..45c9ae0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -647,7 +647,7 @@
     /**
      * @return The left bounds of the location bar after accounting for any visible left buttons.
      */
-    protected int getBoundsAfterAccountingForLeftButton() {
+    private int getBoundsAfterAccountingForLeftButton() {
         int padding = mToolbarSidePadding;
         if (mHomeButton.getVisibility() != GONE) padding = mHomeButton.getMeasuredWidth();
         return padding;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index c21d9c7..3067695 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -260,7 +260,13 @@
      */
     public static boolean isChromeHomeEnabled() {
         if (sChromeHomeEnabled == null) {
-            sChromeHomeEnabled = ChromePreferenceManager.getInstance().isChromeHomeEnabled();
+            // Allow disk access for preferences while Chrome Home is in experimentation.
+            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            try {
+                sChromeHomeEnabled = ChromePreferenceManager.getInstance().isChromeHomeEnabled();
+            } finally {
+                StrictMode.setThreadPolicy(oldPolicy);
+            }
         }
 
         return sChromeHomeEnabled;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java
new file mode 100644
index 0000000..7683397
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java
@@ -0,0 +1,44 @@
+// 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.chrome.browser.vr_shell;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Container class to provide the version and the compatibility with Chrome of the installed VrCore.
+ */
+@JNINamespace("vr_shell")
+public class VrCoreInfo {
+    /** Represents the version of the installed GVR SDK. */
+    public static class GvrVersion {
+        public final int majorVersion;
+        public final int minorVersion;
+        public final int patchVersion;
+
+        public GvrVersion(int majorVersion, int minorVersion, int patchVersion) {
+            this.majorVersion = majorVersion;
+            this.minorVersion = minorVersion;
+            this.patchVersion = patchVersion;
+        }
+    }
+
+    public final GvrVersion gvrVersion;
+    @VrCoreCompatibility
+    public final int compatibility;
+
+    public VrCoreInfo(GvrVersion gvrVersion, int compatibility) {
+        this.gvrVersion = gvrVersion;
+        this.compatibility = compatibility;
+    }
+
+    public long makeNativeVrCoreInfo() {
+        return (gvrVersion == null) ? nativeInit(0, 0, 0, compatibility)
+                                    : nativeInit(gvrVersion.majorVersion, gvrVersion.minorVersion,
+                                              gvrVersion.patchVersion, compatibility);
+    }
+
+    private native long nativeInit(
+            int majorVersion, int minorVersion, int patchVersion, int compatibility);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java
index e6f6fbf..46852a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java
@@ -4,29 +4,16 @@
 
 package org.chromium.chrome.browser.vr_shell;
 
-import android.support.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Abstracts away the VrCoreVersionCheckerImpl class, which may or may not be present at runtime
  * depending on compile flags.
  */
 public interface VrCoreVersionChecker {
-    public static final int VR_NOT_SUPPORTED = 0;
-    public static final int VR_NOT_AVAILABLE = 1;
-    public static final int VR_OUT_OF_DATE = 2;
-    public static final int VR_READY = 3;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({VR_NOT_AVAILABLE, VR_OUT_OF_DATE, VR_READY})
-    public @interface VrCoreCompatibility {}
-
     public static final String VR_CORE_PACKAGE_ID = "com.google.vr.vrcore";
 
     /**
-     * Check if VrCore is installed or if installed version is compatible with Chromium.
+     * Returns the version of VrCore (if it is installed) and the compatibility of VrCore with
+     * Chrome.
      */
-    int getVrCoreCompatibility();
+    VrCoreInfo getVrCoreInfo();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java
index 6ab0169..72fec4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionCheckerImpl.java
@@ -13,8 +13,8 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.PackageUtils;
-
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.vr_shell.VrCoreInfo.GvrVersion;
 
 /**
  * Helper class to check if VrCore version is compatible with Chromium.
@@ -25,14 +25,14 @@
     private static final String MIN_SDK_VERSION_PARAM_NAME = "min_sdk_version";
 
     @Override
-    public int getVrCoreCompatibility() {
+    public VrCoreInfo getVrCoreInfo() {
         // Supported Build version is determined by the webvr cardboard support feature.
         // Default is KITKAT unless specified via server side finch config.
         if (Build.VERSION.SDK_INT < ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
                                             ChromeFeatureList.WEBVR_CARDBOARD_SUPPORT,
                                             MIN_SDK_VERSION_PARAM_NAME,
                                             Build.VERSION_CODES.KITKAT)) {
-            return VrCoreVersionChecker.VR_NOT_SUPPORTED;
+            return new VrCoreInfo(null, VrCoreCompatibility.VR_NOT_SUPPORTED);
         }
         try {
             String vrCoreSdkLibraryVersionString = VrCoreUtils.getVrCoreSdkLibraryVersion(
@@ -40,10 +40,12 @@
             Version vrCoreSdkLibraryVersion = Version.parse(vrCoreSdkLibraryVersionString);
             Version targetSdkLibraryVersion =
                     Version.parse(com.google.vr.ndk.base.BuildConstants.VERSION);
+            GvrVersion gvrVersion = new GvrVersion(vrCoreSdkLibraryVersion.majorVersion,
+                    vrCoreSdkLibraryVersion.minorVersion, vrCoreSdkLibraryVersion.patchVersion);
             if (!vrCoreSdkLibraryVersion.isAtLeast(targetSdkLibraryVersion)) {
-                return VrCoreVersionChecker.VR_OUT_OF_DATE;
+                return new VrCoreInfo(gvrVersion, VrCoreCompatibility.VR_OUT_OF_DATE);
             }
-            return VrCoreVersionChecker.VR_READY;
+            return new VrCoreInfo(gvrVersion, VrCoreCompatibility.VR_READY);
         } catch (VrCoreNotAvailableException e) {
             Log.i(TAG, "Unable to find VrCore.");
             // Old versions of VrCore are not integrated with the sdk library version check and will
@@ -53,9 +55,9 @@
             if (PackageUtils.getPackageVersion(
                         ContextUtils.getApplicationContext(), VR_CORE_PACKAGE_ID)
                     != -1) {
-                return VrCoreVersionChecker.VR_OUT_OF_DATE;
+                return new VrCoreInfo(null, VrCoreCompatibility.VR_OUT_OF_DATE);
             }
-            return VrCoreVersionChecker.VR_NOT_AVAILABLE;
+            return new VrCoreInfo(null, VrCoreCompatibility.VR_NOT_AVAILABLE);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index 52d0523..b0121f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -139,6 +139,7 @@
     // Best effort whether or not the system was in VR when Chrome launched.
     private Boolean mInVrAtChromeLaunch;
     private boolean mShowingDaydreamDoff;
+    private boolean mDoffOptional;
     private boolean mExitingCct;
     private boolean mPaused;
     private int mRestoreSystemUiVisibilityFlag = -1;
@@ -483,7 +484,6 @@
                 if (activity == mActivity) cancelPendingVrEntry();
                 break;
             case ActivityState.RESUMED:
-                assert !mInVr || mShowingDaydreamDoff;
                 if (mInVr && activity != mActivity) {
                     if (mShowingDaydreamDoff) {
                         onExitVrResult(true);
@@ -507,6 +507,7 @@
     // activity.
     private void swapHostActivity(ChromeActivity activity) {
         assert mActivity != null;
+        if (mActivity == activity) return;
         mActivity = activity;
         mVrDaydreamApi = mVrClassesWrapper.createVrDaydreamApi(mActivity);
         if (mNativeVrShellDelegate == 0 || mNonPresentingGvrContext == null) return;
@@ -780,11 +781,7 @@
         if (!mInVr) return false;
         if (!isVrShellEnabled(mVrSupportLevel) || !isDaydreamCurrentViewer()
                 || !activitySupportsVrBrowsing(mActivity)) {
-            if (isDaydreamCurrentViewer()
-                    && mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
-                mShowingDaydreamDoff = true;
-                return false;
-            }
+            if (isDaydreamCurrentViewer() && showDoff(false /* optional */)) return false;
             shutdownVr(
                     true /* disableVrMode */, false /* canReenter */, true /* stayingInChrome */);
         } else {
@@ -797,8 +794,6 @@
     private void resumeVr() {
         mPaused = false;
 
-        assert !mInVr || mShowingDaydreamDoff;
-
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         try {
             if (mNativeVrShellDelegate != 0) nativeOnResume(mNativeVrShellDelegate);
@@ -886,6 +881,14 @@
         return true;
     }
 
+    private boolean showDoff(boolean optional) {
+        if (!isDaydreamCurrentViewer()) return false;
+        if (!mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) return false;
+        mShowingDaydreamDoff = true;
+        mDoffOptional = optional;
+        return true;
+    }
+
     private void onExitVrResult(boolean success) {
         assert mVrSupportLevel != VR_NOT_AVAILABLE;
 
@@ -894,14 +897,14 @@
         // real DOFF flow calls us back.
         if (!mShowingDaydreamDoff) return;
 
-        // For now, we don't handle re-entering VR when exit fails, so keep trying to exit.
-        if (!success && mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) return;
-
+        // If Doff is not optional and user backed out, keep trying to exit.
+        if (!mDoffOptional && !success && showDoff(false /* optional */)) return;
         mShowingDaydreamDoff = false;
-
-        shutdownVr(true /* disableVrMode */, false /* canReenter */,
-                !mExitingCct /* stayingInChrome */);
-        if (mExitingCct) ((CustomTabActivity) mActivity).finishAndClose(false);
+        if (success) {
+            shutdownVr(true /* disableVrMode */, false /* canReenter */,
+                    !mExitingCct /* stayingInChrome */);
+            if (mExitingCct) ((CustomTabActivity) mActivity).finishAndClose(false);
+        }
         mExitingCct = false;
     }
 
@@ -989,10 +992,7 @@
 
     /* package */ void showDoffAndExitVr() {
         if (mShowingDaydreamDoff) return;
-        if (mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
-            mShowingDaydreamDoff = true;
-            return;
-        }
+        if (showDoff(false /* optional */)) return;
         shutdownVr(true /* disableVrMode */, false /* canReenter */, true /* stayingInChrome */);
     }
 
@@ -1000,11 +1000,13 @@
         if (mShowingDaydreamDoff) return;
         assert mActivity instanceof CustomTabActivity;
         if (mInVrAtChromeLaunch != null && !mInVrAtChromeLaunch) {
-            if (mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
+            if (showDoff(true /* optional */)) {
                 mExitingCct = true;
-                mShowingDaydreamDoff = true;
                 return;
             }
+            shutdownVr(
+                    true /* disableVrMode */, false /* canReenter */, false /* stayingInChrome */);
+            ((CustomTabActivity) mActivity).finishAndClose(false);
         }
     }
 
@@ -1066,14 +1068,14 @@
 
     private static boolean isVrCoreCompatible(
             VrCoreVersionChecker versionChecker, Tab tabToShowInfobarIn) {
-        int vrCoreCompatibility = versionChecker.getVrCoreCompatibility();
+        int vrCoreCompatibility = versionChecker.getVrCoreInfo().compatibility;
 
-        if (vrCoreCompatibility == VrCoreVersionChecker.VR_NOT_AVAILABLE
-                || vrCoreCompatibility == VrCoreVersionChecker.VR_OUT_OF_DATE) {
+        if (vrCoreCompatibility == VrCoreCompatibility.VR_NOT_AVAILABLE
+                || vrCoreCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE) {
             promptToUpdateVrServices(vrCoreCompatibility, tabToShowInfobarIn);
         }
 
-        return vrCoreCompatibility == VrCoreVersionChecker.VR_READY;
+        return vrCoreCompatibility == VrCoreCompatibility.VR_READY;
     }
 
     private static void promptToUpdateVrServices(int vrCoreCompatibility, Tab tab) {
@@ -1083,11 +1085,11 @@
         final Activity activity = tab.getActivity();
         String infobarText;
         String buttonText;
-        if (vrCoreCompatibility == VrCoreVersionChecker.VR_NOT_AVAILABLE) {
+        if (vrCoreCompatibility == VrCoreCompatibility.VR_NOT_AVAILABLE) {
             // Supported, but not installed. Ask user to install instead of upgrade.
             infobarText = activity.getString(R.string.vr_services_check_infobar_install_text);
             buttonText = activity.getString(R.string.vr_services_check_infobar_install_button);
-        } else if (vrCoreCompatibility == VrCoreVersionChecker.VR_OUT_OF_DATE) {
+        } else if (vrCoreCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE) {
             infobarText = activity.getString(R.string.vr_services_check_infobar_update_text);
             buttonText = activity.getString(R.string.vr_services_check_infobar_update_button);
         } else {
@@ -1231,6 +1233,12 @@
         return mNativeVrShellDelegate;
     }
 
+    @CalledByNative
+    private long getVrCoreInfo() {
+        assert mVrCoreVersionChecker != null;
+        return mVrCoreVersionChecker.getVrCoreInfo().makeNativeVrCoreInfo();
+    }
+
     private void destroy() {
         if (sInstance == null) return;
         shutdownVr(false /* disableVrMode */, false /* canReenter */, false /* stayingInChrome */);
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 4649655..4a03177 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1149,6 +1149,7 @@
   "java/src/org/chromium/chrome/browser/util/ViewUtils.java",
   "java/src/org/chromium/chrome/browser/vr_shell/NonPresentingGvrContext.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrClassesWrapper.java",
+  "java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrCoreVersionChecker.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java",
   "java/src/org/chromium/chrome/browser/vr_shell/VrFeedbackStatus.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
index 2a7c1dc..5ed67d7c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
@@ -431,6 +431,7 @@
 
     @SmallTest
     @Feature({"Download"})
+    @RetryOnFailure
     public void testServiceWillStopOnCompletedDownload() throws Exception {
         // On versions of Android that use a foreground service, the service will currently die with
         // the notifications.
@@ -450,6 +451,7 @@
 
     @SmallTest
     @Feature({"Download"})
+    @RetryOnFailure
     public void testServiceWillStopOnFailedDownload() throws Exception {
         // On versions of Android that use a foreground service, the service will currently die with
         // the notifications.
@@ -468,6 +470,7 @@
 
     @SmallTest
     @Feature({"Download"})
+    @RetryOnFailure
     public void testServiceWillStopOnCancelledDownload() throws Exception {
         // On versions of Android that use a foreground service, the service will currently die with
         // the notifications.
@@ -522,6 +525,7 @@
 
     @SmallTest
     @Feature({"Download"})
+    @RetryOnFailure
     public void testServiceWillNotStopWithOneOngoingDownload() throws Exception {
         // On versions of Android that use a foreground service, the service will currently die with
         // the notifications.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/MockVrCoreVersionCheckerImpl.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/MockVrCoreVersionCheckerImpl.java
index 1e35cec8..50221fe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/MockVrCoreVersionCheckerImpl.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/MockVrCoreVersionCheckerImpl.java
@@ -10,21 +10,20 @@
  */
 public class MockVrCoreVersionCheckerImpl extends VrCoreVersionCheckerImpl {
     private boolean mUseActualImplementation;
-    private int mMockReturnValue = VrCoreVersionChecker.VR_READY;
-    private int mLastReturnValue = -1;
+    private VrCoreInfo mMockReturnValue = new VrCoreInfo(null, VrCoreCompatibility.VR_READY);
+    private VrCoreInfo mLastReturnValue = null;
 
     @Override
-    public int getVrCoreCompatibility() {
-        mLastReturnValue =
-                mUseActualImplementation ? super.getVrCoreCompatibility() : mMockReturnValue;
+    public VrCoreInfo getVrCoreInfo() {
+        mLastReturnValue = mUseActualImplementation ? super.getVrCoreInfo() : mMockReturnValue;
         return mLastReturnValue;
     }
 
-    public int getLastReturnValue() {
+    public VrCoreInfo getLastReturnValue() {
         return mLastReturnValue;
     }
 
-    public void setMockReturnValue(int value) {
+    public void setMockReturnValue(VrCoreInfo value) {
         mMockReturnValue = value;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
index d436ba18..309ba1c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
@@ -181,26 +181,26 @@
      * Helper function to run the tests checking for the upgrade/install InfoBar being present since
      * all that differs is the value returned by VrCoreVersionChecker and a couple asserts.
      *
-     * @param checkerReturnValue The value to have the VrCoreVersionChecker return
+     * @param checkerReturnCompatibility The compatibility to have the VrCoreVersionChecker return
      */
-    private void infoBarTestHelper(int checkerReturnValue) throws InterruptedException {
+    private void infoBarTestHelper(int checkerReturnCompatibility) throws InterruptedException {
         MockVrCoreVersionCheckerImpl mockChecker = new MockVrCoreVersionCheckerImpl();
-        mockChecker.setMockReturnValue(checkerReturnValue);
+        mockChecker.setMockReturnValue(new VrCoreInfo(null, checkerReturnCompatibility));
         VrUtils.getVrShellDelegateInstance().overrideVrCoreVersionCheckerForTesting(mockChecker);
         mVrTestRule.loadUrlAndAwaitInitialization(
                 VrTestRule.getHtmlTestFile("generic_webvr_page"), PAGE_LOAD_TIMEOUT_S);
         String displayFound = "VRDisplay Found";
         String barPresent = "InfoBar present";
-        if (checkerReturnValue == VrCoreVersionChecker.VR_READY) {
+        if (checkerReturnCompatibility == VrCoreCompatibility.VR_READY) {
             Assert.assertTrue(
                     displayFound, mVrTestRule.vrDisplayFound(mVrTestRule.getFirstTabWebContents()));
             Assert.assertFalse(barPresent,
                     VrUtils.isInfoBarPresent(mVrTestRule.getActivity().getWindow().getDecorView()));
-        } else if (checkerReturnValue == VrCoreVersionChecker.VR_OUT_OF_DATE
-                || checkerReturnValue == VrCoreVersionChecker.VR_NOT_AVAILABLE) {
+        } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE
+                || checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_AVAILABLE) {
             // Out of date and missing cases are the same, but with different text
             String expectedMessage, expectedButton;
-            if (checkerReturnValue == VrCoreVersionChecker.VR_OUT_OF_DATE) {
+            if (checkerReturnCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE) {
                 expectedMessage = mVrTestRule.getActivity().getString(
                         R.string.vr_services_check_infobar_update_text);
                 expectedButton = mVrTestRule.getActivity().getString(
@@ -222,16 +222,17 @@
             tempView = (TextView) mVrTestRule.getActivity().getWindow().getDecorView().findViewById(
                     R.id.button_primary);
             Assert.assertEquals(expectedButton, tempView.getText().toString());
-        } else if (checkerReturnValue == VrCoreVersionChecker.VR_NOT_SUPPORTED) {
+        } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_SUPPORTED) {
             Assert.assertFalse(
                     displayFound, mVrTestRule.vrDisplayFound(mVrTestRule.getFirstTabWebContents()));
             Assert.assertFalse(barPresent,
                     VrUtils.isInfoBarPresent(mVrTestRule.getActivity().getWindow().getDecorView()));
         } else {
-            Assert.fail(
-                    "Invalid VrCoreVersionChecker value: " + String.valueOf(checkerReturnValue));
+            Assert.fail("Invalid VrCoreVersionChecker compatibility: "
+                    + String.valueOf(checkerReturnCompatibility));
         }
-        Assert.assertEquals(checkerReturnValue, mockChecker.getLastReturnValue());
+        Assert.assertEquals(
+                checkerReturnCompatibility, mockChecker.getLastReturnValue().compatibility);
     }
 
     /**
@@ -241,7 +242,7 @@
     @Test
     @MediumTest
     public void testInfoBarNotPresentWhenVrServicesCurrent() throws InterruptedException {
-        infoBarTestHelper(VrCoreVersionChecker.VR_READY);
+        infoBarTestHelper(VrCoreCompatibility.VR_READY);
     }
 
     /**
@@ -250,7 +251,7 @@
     @Test
     @MediumTest
     public void testInfoBarPresentWhenVrServicesOutdated() throws InterruptedException {
-        infoBarTestHelper(VrCoreVersionChecker.VR_OUT_OF_DATE);
+        infoBarTestHelper(VrCoreCompatibility.VR_OUT_OF_DATE);
     }
 
     /**
@@ -259,7 +260,7 @@
     @Test
     @MediumTest
     public void testInfoBarPresentWhenVrServicesMissing() throws InterruptedException {
-        infoBarTestHelper(VrCoreVersionChecker.VR_NOT_AVAILABLE);
+        infoBarTestHelper(VrCoreCompatibility.VR_NOT_AVAILABLE);
     }
 
     /**
@@ -269,7 +270,7 @@
     @Test
     @MediumTest
     public void testInfoBarNotPresentWhenVrServicesNotSupported() throws InterruptedException {
-        infoBarTestHelper(VrCoreVersionChecker.VR_NOT_SUPPORTED);
+        infoBarTestHelper(VrCoreCompatibility.VR_NOT_SUPPORTED);
     }
 
     /**
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 7e40b44e..5797be91 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -368,6 +368,7 @@
   if (enable_package_mash_services) {
     deps += [
       "//chrome/app/mash:chrome_mash_catalog",
+      "//chrome/app/mash:chrome_test_catalog",
       "//chrome/app/mash:embedded_services",
       "//mash/common",
       "//mash/quick_launch/public/interfaces:constants",
@@ -419,6 +420,10 @@
   ]
 }
 
+service_manifest("chrome_test_browser_overlay") {
+  source = "//chrome/browser/chrome_test_browser_overlay.json"
+}
+
 service_manifest("chrome_content_gpu_manifest_overlay") {
   source = "//chrome/browser/chrome_content_gpu_manifest_overlay.json"
 }
@@ -472,6 +477,11 @@
     overlays = [ ":chrome_content_browser_manifest_overlay" ]
   }
 
+  service_manifest("chrome_test_browser_manifest") {
+    source_manifest = ":chrome_content_browser_manifest"
+    overlays = [ ":chrome_test_browser_overlay" ]
+  }
+
   service_manifest("chrome_content_gpu_manifest") {
     source_manifest = "//content/public/app:gpu_manifest"
     overlays = [ ":chrome_content_gpu_manifest_overlay" ]
@@ -524,4 +534,12 @@
         chrome_embedded_services +
         [ ":chrome_content_packaged_services_manifest_for_mash" ]
   }
+
+  catalog("catalog_for_tests_mash") {
+    embedded_services = chrome_embedded_services + [
+                          ":chrome_content_packaged_services_manifest_for_mash",
+                          ":chrome_test_browser_manifest",
+                        ]
+    embedded_services -= [ ":chrome_content_browser_manifest" ]
+  }
 }
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index d105144..1b6ad13b 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -193,6 +193,9 @@
 
 namespace {
 
+base::LazyInstance<ChromeMainDelegate::ServiceCatalogFactory>::Leaky
+    g_service_catalog_factory = LAZY_INSTANCE_INITIALIZER;
+
 #if defined(OS_WIN)
 // Early versions of Chrome incorrectly registered a chromehtml: URL handler,
 // which gives us nothing but trouble. Avoid launching chrome this way since
@@ -523,6 +526,12 @@
 ChromeMainDelegate::~ChromeMainDelegate() {
 }
 
+// static
+void ChromeMainDelegate::InstallServiceCatalogFactory(
+    ServiceCatalogFactory factory) {
+  g_service_catalog_factory.Get() = std::move(factory);
+}
+
 bool ChromeMainDelegate::BasicStartupComplete(int* exit_code) {
 #if defined(OS_CHROMEOS)
   chromeos::BootTimesRecorder::Get()->SaveChromeMainStats();
@@ -1128,6 +1137,8 @@
 }
 
 std::unique_ptr<base::Value> ChromeMainDelegate::CreateServiceCatalog() {
+  if (!g_service_catalog_factory.Get().is_null())
+    return g_service_catalog_factory.Get().Run();
 #if BUILDFLAG(ENABLE_PACKAGE_MASH_SERVICES)
   const auto& command_line = *base::CommandLine::ForCurrentProcess();
 #if defined(OS_CHROMEOS)
diff --git a/chrome/app/chrome_main_delegate.h b/chrome/app/chrome_main_delegate.h
index 22ba58a..aaf785e1 100644
--- a/chrome/app/chrome_main_delegate.h
+++ b/chrome/app/chrome_main_delegate.h
@@ -28,6 +28,10 @@
   explicit ChromeMainDelegate(base::TimeTicks exe_entry_point_ticks);
   ~ChromeMainDelegate() override;
 
+  using ServiceCatalogFactory =
+      base::RepeatingCallback<std::unique_ptr<base::Value>(void)>;
+  static void InstallServiceCatalogFactory(ServiceCatalogFactory factory);
+
  protected:
   // content::ContentMainDelegate implementation:
   bool BasicStartupComplete(int* exit_code) override;
diff --git a/chrome/app/chrome_test_exe_main_aura.cc b/chrome/app/chrome_test_exe_main_aura.cc
new file mode 100644
index 0000000..4a8f098
--- /dev/null
+++ b/chrome/app/chrome_test_exe_main_aura.cc
@@ -0,0 +1,17 @@
+// 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 "build/build_config.h"
+#include "chrome/app/chrome_main_delegate.h"
+#include "chrome/app/mash/chrome_test_catalog.h"
+
+extern "C" {
+int ChromeMain(int argc, const char** argv);
+}
+
+int main(int argc, const char** argv) {
+  ChromeMainDelegate::InstallServiceCatalogFactory(
+      base::Bind(&CreateChromeTestCatalog));
+  return ChromeMain(argc, argv);
+}
diff --git a/chrome/app/mash/BUILD.gn b/chrome/app/mash/BUILD.gn
index f6f8e38..cd6f864 100644
--- a/chrome/app/mash/BUILD.gn
+++ b/chrome/app/mash/BUILD.gn
@@ -65,7 +65,7 @@
   }
 }
 
-catalog("catalog") {
+catalog("catalog_common") {
   embedded_services = [
     "//ash/autoclick/mus:manifest",
     "//ash/touch_hud/mus:manifest",
@@ -76,7 +76,6 @@
     "//services/ui:manifest",
     "//services/ui/ime/test_ime_driver:manifest",
   ]
-  catalog_deps = [ "//chrome/app:catalog_for_mash" ]
   standalone_services = [
     "//mash/example/views_examples:manifest",
     "//mash/simple_wm:manifest",
@@ -95,11 +94,30 @@
   }
 }
 
+catalog("catalog") {
+  catalog_deps = [
+    ":catalog_common",
+    "//chrome/app:catalog_for_mash",
+  ]
+}
+
 catalog_cpp_source("chrome_mash_catalog") {
   catalog = ":catalog"
   generated_function_name = "CreateChromeMashCatalog"
 }
 
+catalog("catalog_test") {
+  catalog_deps = [
+    ":catalog_common",
+    "//chrome/app:catalog_for_tests_mash",
+  ]
+}
+
+catalog_cpp_source("chrome_test_catalog") {
+  catalog = ":catalog_test"
+  generated_function_name = "CreateChromeTestCatalog"
+}
+
 if (is_chromeos) {
   catalog("catalog_mus") {
     catalog_deps = [ "//chrome/app:catalog" ]
diff --git a/chrome/app/media_router_strings.grdp b/chrome/app/media_router_strings.grdp
index 97bc13c..df3bbc6 100644
--- a/chrome/app/media_router_strings.grdp
+++ b/chrome/app/media_router_strings.grdp
@@ -45,14 +45,23 @@
   <message name="IDS_MEDIA_ROUTER_TAB_MIRROR_CAST_MODE" desc="Title for the tab mirror cast mode. This is shown as a dropdown option, and if selected, also appears as the header of a list of devices.">
     Cast tab
   </message>
+  <message name="IDS_MEDIA_ROUTER_LOCAL_FILE_CAST_MODE" desc="Title for choosing a file to cast. This is shown as a dropdown option.">
+    Cast file
+  </message>
 
   <!-- Cast Mode Picker -->
   <message name="IDS_MEDIA_ROUTER_SELECT_CAST_MODE_HEADER" desc="Header for the select cast mode view, which shows a list of cast modes that the user can select.">
    Select source
   </message>
-  <message name="IDS_MEDIA_ROUTER_SHARE_YOUR_SCREEN_SUBHEADING" desc="Subheading for non-default cast modes, which allow screen sharing. The subheading appears above a list of non-default cast modes.">
+  <message name="IDS_MEDIA_ROUTER_SHARE_YOUR_SCREEN_SUBHEADING" desc="Subheading for screen sharing cast modes. The subheading appears above a list of screen sharing cast modes.">
    Share your screen
   </message>
+  <message name="IDS_MEDIA_ROUTER_CAST_LOCAL_MEDIA_SUBHEADING" desc="Subheading for local file cast modes. The subheading appears above a list of media cast modes.">
+   Stream a video or audio file
+  </message>
+  <message name="IDS_MEDIA_ROUTER_CAST_LOCAL_MEDIA_TITLE" desc="Title of the local file cast mode after a local file is selected for casting.">
+   Cast <ph name="FILE_NAME">$1<ex>my_media.mp3</ex></ph>
+  </message>
 
   <!-- Contextual Menu -->
   <message name="IDS_MEDIA_ROUTER_ABOUT" desc="Title of a menu item which, on click, opens a page with product information about Chromecast.">
diff --git a/chrome/app/vector_icons/browser_tools_animated.1x.icon b/chrome/app/vector_icons/browser_tools_animated.1x.icon
index 18abb617..01566f2 100644
--- a/chrome/app/vector_icons/browser_tools_animated.1x.icon
+++ b/chrome/app/vector_icons/browser_tools_animated.1x.icon
@@ -28,10 +28,10 @@
 TRANSITION_TO,
 MOVE_TO, 7, 3.5f,
 CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
-LINE_TO, 8.5f, 2,
+R_H_LINE_TO, 0,
 CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
 CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
-LINE_TO, 8.5f, 5,
+R_H_LINE_TO, 0,
 CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
 TRANSITION_END, 416, 533, gfx::Tween::FAST_OUT_SLOW_IN,
 
@@ -101,7 +101,7 @@
 CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
 R_H_LINE_TO, 0,
 CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
-TRANSITION_END, 283, 400,  gfx::Tween::FAST_OUT_SLOW_IN,
+TRANSITION_END, 283, 400, gfx::Tween::FAST_OUT_SLOW_IN,
 
 CLOSE,
 END
diff --git a/chrome/app/vector_icons/browser_tools_animated.icon b/chrome/app/vector_icons/browser_tools_animated.icon
index c1e7dc7..ba7adb98 100644
--- a/chrome/app/vector_icons/browser_tools_animated.icon
+++ b/chrome/app/vector_icons/browser_tools_animated.icon
@@ -2,40 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(estade): this is copied from 1x. Instead, apply motion to the unanimated
-// 2x asset.
-
-CANVAS_DIMENSIONS, 16,
+CANVAS_DIMENSIONS, 32,
 
 // Top dot.
 TRANSITION_FROM,
 TRANSITION_FROM,
-MOVE_TO, 7, 3.5f,
-CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
+MOVE_TO, 16, 9,
+CUBIC_TO, 17.66f, 9, 19, 7.66f, 19, 6,
+CUBIC_TO, 19, 4.34f, 17.66f, 3, 16, 3,
 R_H_LINE_TO, 0,
-CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
-CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
-R_H_LINE_TO, 0,
-CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
+CUBIC_TO, 14.34f, 3, 13, 4.34f, 13, 6,
+CUBIC_TO, 13, 7.66f, 14.34f, 9, 16, 9,
 
 TRANSITION_TO,
-MOVE_TO, 4.68f, 3.5f,
-CUBIC_TO, 4.68f, 2.95f, 5.13f, 2.5f, 5.68f, 2.5f,
-R_H_LINE_TO, 5.65f,
-CUBIC_TO, 11.87f, 2.5f, 12.32f, 2.95f, 12.32f, 3.5f,
-CUBIC_TO, 12.32f, 4.05f, 11.87f, 4.5f, 11.32f, 4.5f,
-R_H_LINE_TO, -5.65f,
-CUBIC_TO, 5.13f, 4.5f, 4.68f, 4.05f, 4.68f, 2.5f,
+MOVE_TO, 21.65f, 8,
+CUBIC_TO, 22.76f, 8, 23.65f, 6.66f, 23.65f, 6,
+CUBIC_TO, 23.65f, 5.34f, 22.76f, 4, 21.65f, 4,
+R_H_LINE_TO, -11.29f,
+CUBIC_TO, 9.24f, 4, 8.35f, 5.34f, 8.35f, 6,
+CUBIC_TO, 8.35f, 6.66f, 9.24f, 8, 10.35f, 8,
 TRANSITION_END, 133, 150, gfx::Tween::FAST_OUT_SLOW_IN,
 
 TRANSITION_TO,
-MOVE_TO, 7, 3.5f,
-CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
-LINE_TO, 8.5f, 2,
-CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
-CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
-LINE_TO, 8.5f, 5,
-CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
+MOVE_TO, 16, 9,
+CUBIC_TO, 17.66f, 9, 19, 7.66f, 19, 6,
+CUBIC_TO, 19, 4.34f, 17.66f, 3, 16, 3,
+R_H_LINE_TO, 0,
+CUBIC_TO, 14.34f, 3, 13, 4.34f, 13, 6,
+CUBIC_TO, 13, 7.66f, 14.34f, 9, 16, 9,
 TRANSITION_END, 416, 533, gfx::Tween::FAST_OUT_SLOW_IN,
 
 CLOSE,
@@ -44,32 +38,29 @@
 // Middle dot.
 TRANSITION_FROM,
 TRANSITION_FROM,
-MOVE_TO, 7, 8.5f,
-CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
+MOVE_TO, 16, 19,
+CUBIC_TO, 17.66f, 19, 19, 17.66f, 19, 16,
+CUBIC_TO, 19, 14.34f, 17.66f, 13, 16, 13,
 R_H_LINE_TO, 0,
-CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
-CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
-R_H_LINE_TO, 0,
-CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
+CUBIC_TO, 14.34f, 13, 13, 14.34f, 13, 16,
+CUBIC_TO, 13, 17.66f, 14.34f, 19, 16, 19,
 
 TRANSITION_TO,
-MOVE_TO, 4.68f, 8.5f,
-CUBIC_TO, 4.68f, 7.95f, 5.13f, 7.5f, 5.68f, 7.5f,
-R_H_LINE_TO, 5.65f,
-CUBIC_TO, 11.87f, 7.5f, 12.32f, 7.95f, 12.32f, 8.5f,
-CUBIC_TO, 12.32f, 9.05f, 11.87f, 9.5f, 11.32f, 9.5f,
-R_H_LINE_TO, -5.65f,
-CUBIC_TO, 5.13f, 9.5f, 4.68f, 9.05f, 4.68f, 7.5f,
+MOVE_TO, 21.65f, 18,
+CUBIC_TO, 22.76f, 18, 23.65f, 16.66f, 23.65f, 16,
+CUBIC_TO, 23.65f, 15.34f, 22.76f, 14, 21.65f, 14,
+R_H_LINE_TO, -11.29f,
+CUBIC_TO, 9.24f, 14, 8.35f, 15.34f, 8.35f, 16,
+CUBIC_TO, 8.35f, 16.66f, 9.24f, 18, 10.35f, 18,
 TRANSITION_END, 67, 150, gfx::Tween::FAST_OUT_SLOW_IN,
 
 TRANSITION_TO,
-MOVE_TO, 7, 8.5f,
-CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
+MOVE_TO, 16, 19,
+CUBIC_TO, 17.66f, 19, 19, 17.66f, 19, 16,
+CUBIC_TO, 19, 14.34f, 17.66f, 13, 16, 13,
 R_H_LINE_TO, 0,
-CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
-CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
-R_H_LINE_TO, 0,
-CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
+CUBIC_TO, 14.34f, 13, 13, 14.34f, 13, 16,
+CUBIC_TO, 13, 17.66f, 14.34f, 19, 16, 19,
 TRANSITION_END, 350, 383, gfx::Tween::FAST_OUT_SLOW_IN,
 
 CLOSE,
@@ -78,33 +69,30 @@
 // Bottom dot.
 TRANSITION_FROM,
 TRANSITION_FROM,
-MOVE_TO, 7, 13.5f,
-CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
+MOVE_TO, 16, 29,
+CUBIC_TO, 17.66f, 29, 19, 27.66f, 19, 26,
+CUBIC_TO, 19, 24.34f, 17.66f, 23, 16, 23,
 R_H_LINE_TO, 0,
-CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
-CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
-R_H_LINE_TO, 0,
-CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
+CUBIC_TO, 14.34f, 23, 13, 24.34f, 13, 26,
+CUBIC_TO, 13, 27.66f, 14.34f, 29, 16, 29,
 
 TRANSITION_TO,
-MOVE_TO, 4.68f, 13.5f,
-CUBIC_TO, 4.68f, 12.95f, 5.13f, 12.5f, 5.68f, 12.5f,
-R_H_LINE_TO, 5.65f,
-CUBIC_TO, 11.87f, 12.5f, 12.32f, 12.95f, 12.32f, 13.5f,
-CUBIC_TO, 12.32f, 14.05f, 11.87f, 14.5f, 11.32f, 14.5f,
-R_H_LINE_TO, -5.65f,
-CUBIC_TO, 5.13f, 14.5f, 4.68f, 14.05f, 4.68f, 12.5f,
+MOVE_TO, 21.65f, 28,
+CUBIC_TO, 22.76f, 28, 23.65f, 26.66f, 23.65f, 26,
+CUBIC_TO, 23.65f, 25.34f, 22.76f, 24, 21.65f, 24,
+R_H_LINE_TO, -11.29f,
+CUBIC_TO, 9.24f, 24, 8.35f, 25.34f, 8.35f, 26,
+CUBIC_TO, 8.35f, 26.66f, 9.24f, 28, 10.35f, 28,
 TRANSITION_END, 0, 150, gfx::Tween::FAST_OUT_SLOW_IN,
 
 TRANSITION_TO,
-MOVE_TO, 7, 13.5f,
-CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
+MOVE_TO, 16, 29,
+CUBIC_TO, 17.66f, 29, 19, 27.66f, 19, 26,
+CUBIC_TO, 19, 24.34f, 17.66f, 23, 16, 23,
 R_H_LINE_TO, 0,
-CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
-CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
-R_H_LINE_TO, 0,
-CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
-TRANSITION_END, 283, 400,  gfx::Tween::FAST_OUT_SLOW_IN,
+CUBIC_TO, 14.34f, 23, 13, 24.34f, 13, 26,
+CUBIC_TO, 13, 27.66f, 14.34f, 29, 16, 29,
+TRANSITION_END, 283, 400, gfx::Tween::FAST_OUT_SLOW_IN,
 
 CLOSE,
-END
+END,
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 07d3d0b..62903eec 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -205,6 +205,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_VR)
+#include "chrome/browser/android/vr_shell/vr_core_info.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_delegate.h"
 #include "third_party/gvr-android-sdk/display_synchronizer_jni.h"
@@ -447,6 +448,7 @@
     {"Variations", variations::android::RegisterVariations},
     {"VariationsSession", chrome::android::RegisterVariationsSession},
 #if BUILDFLAG(ENABLE_VR)
+    {"VrCoreInfo", vr_shell::RegisterVrCoreInfo},
     {"VrShell", vr_shell::RegisterVrShell},
     {"VrShellDelegate", vr_shell::RegisterVrShellDelegate},
     {"DisplaySynchronizer",
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
index 41bd33b..6fd1d02f 100644
--- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -27,8 +27,8 @@
 using ::base::android::ScopedJavaGlobalRef;
 using ::base::android::ScopedJavaLocalRef;
 using ::base::android::ToJavaArrayOfStrings;
-using ::payments::mojom::PaymentAppRequest;
-using ::payments::mojom::PaymentAppRequestPtr;
+using ::payments::mojom::PaymentRequestEventData;
+using ::payments::mojom::PaymentRequestEventDataPtr;
 using ::payments::mojom::PaymentCurrencyAmount;
 using ::payments::mojom::PaymentDetailsModifier;
 using ::payments::mojom::PaymentDetailsModifierPtr;
@@ -101,13 +101,13 @@
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
 
-  PaymentAppRequestPtr app_request = PaymentAppRequest::New();
+  PaymentRequestEventDataPtr event_data = PaymentRequestEventData::New();
 
-  app_request->top_level_origin =
+  event_data->top_level_origin =
       GURL(ConvertJavaStringToUTF8(env, jtop_level_origin));
-  app_request->payment_request_origin =
+  event_data->payment_request_origin =
       GURL(ConvertJavaStringToUTF8(env, jpayment_request_origin));
-  app_request->payment_request_id =
+  event_data->payment_request_id =
       ConvertJavaStringToUTF8(env, jpayment_request_id);
 
   for (jsize i = 0; i < env->GetArrayLength(jmethod_data); i++) {
@@ -124,18 +124,18 @@
         env,
         Java_ServiceWorkerPaymentAppBridge_getStringifiedDataFromMethodData(
             env, element));
-    app_request->method_data.push_back(std::move(methodData));
+    event_data->method_data.push_back(std::move(methodData));
   }
 
-  app_request->total = PaymentItem::New();
-  app_request->total->label = ConvertJavaStringToUTF8(
+  event_data->total = PaymentItem::New();
+  event_data->total->label = ConvertJavaStringToUTF8(
       env,
       Java_ServiceWorkerPaymentAppBridge_getLabelFromPaymentItem(env, jtotal));
-  app_request->total->amount = PaymentCurrencyAmount::New();
-  app_request->total->amount->currency = ConvertJavaStringToUTF8(
+  event_data->total->amount = PaymentCurrencyAmount::New();
+  event_data->total->amount->currency = ConvertJavaStringToUTF8(
       env, Java_ServiceWorkerPaymentAppBridge_getCurrencyFromPaymentItem(
                env, jtotal));
-  app_request->total->amount->value = ConvertJavaStringToUTF8(
+  event_data->total->amount->value = ConvertJavaStringToUTF8(
       env,
       Java_ServiceWorkerPaymentAppBridge_getValueFromPaymentItem(env, jtotal));
 
@@ -173,14 +173,13 @@
         Java_ServiceWorkerPaymentAppBridge_getStringifiedDataFromMethodData(
             env, jmodifier_method_data));
 
-    app_request->modifiers.push_back(std::move(modifier));
+    event_data->modifiers.push_back(std::move(modifier));
   }
 
-  app_request->instrument_key = ConvertJavaStringToUTF8(env, jinstrument_key);
+  event_data->instrument_key = ConvertJavaStringToUTF8(env, jinstrument_key);
 
   content::PaymentAppProvider::GetInstance()->InvokePaymentApp(
-      web_contents->GetBrowserContext(), registration_id,
-      std::move(app_request),
+      web_contents->GetBrowserContext(), registration_id, std::move(event_data),
       base::Bind(&OnPaymentAppInvoked,
                  ScopedJavaGlobalRef<jobject>(env, jweb_contents),
                  ScopedJavaGlobalRef<jobject>(env, jcallback)));
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index cefac4c1..4dedfb9 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -24,6 +24,7 @@
       "font_fallback.h",
       "fps_meter.cc",
       "fps_meter.h",
+      "gl_browser_interface.h",
       "gltf_asset.cc",
       "gltf_asset.h",
       "gltf_parser.cc",
@@ -48,6 +49,7 @@
       "textures/ui_texture.h",
       "textures/url_bar_texture.cc",
       "textures/url_bar_texture.h",
+      "ui_browser_interface.h",
       "ui_elements/audio_capture_indicator.cc",
       "ui_elements/audio_capture_indicator.h",
       "ui_elements/button.cc",
@@ -79,13 +81,14 @@
       "ui_scene_manager.cc",
       "ui_scene_manager.h",
       "ui_unsupported_mode.h",
-      "vr_browser_interface.h",
     ]
 
     deps = [
       "//base",
       "//cc/paint",
+      "//chrome/app:generated_resources",
       "//components/security_state/core",
+      "//components/strings",
       "//components/toolbar:vector_icons",
       "//components/url_formatter",
       "//components/vector_icons",
@@ -101,6 +104,8 @@
     ]
   }
 
+  # This library contains the VR code specific to Android.
+  # TODO(cjgrant): It should be renamed to reflect this.
   static_library("vr_common") {
     defines = []
 
@@ -119,12 +124,16 @@
       "vr_controller.h",
       "vr_controller_model.cc",
       "vr_controller_model.h",
+      "vr_core_info.cc",
+      "vr_core_info.h",
       "vr_gl_thread.cc",
       "vr_gl_thread.h",
       "vr_gl_util.cc",
       "vr_gl_util.h",
       "vr_input_manager.cc",
       "vr_input_manager.h",
+      "vr_metrics_util.cc",
+      "vr_metrics_util.h",
       "vr_shell.cc",
       "vr_shell.h",
       "vr_shell_delegate.cc",
@@ -173,6 +182,7 @@
 
   generate_jni("vr_shell_jni_headers") {
     sources = [
+      "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
     ]
diff --git a/chrome/browser/android/vr_shell/vr_browser_interface.h b/chrome/browser/android/vr_shell/gl_browser_interface.h
similarity index 61%
rename from chrome/browser/android/vr_shell/vr_browser_interface.h
rename to chrome/browser/android/vr_shell/gl_browser_interface.h
index eeb4f1bb..ae79529 100644
--- a/chrome/browser/android/vr_shell/vr_browser_interface.h
+++ b/chrome/browser/android/vr_shell/gl_browser_interface.h
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_VR_BROWSER_INTERFACE_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_VR_BROWSER_INTERFACE_H_
+#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_GL_BROWSER_INTERFACE_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_GL_BROWSER_INTERFACE_H_
 
 #include <memory>
 
 #include "base/android/jni_weak_ref.h"
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
 #include "chrome/browser/android/vr_shell/ui_interface.h"
-#include "chrome/browser/android/vr_shell/ui_unsupported_mode.h"
 #include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
 #include "device/vr/vr_service.mojom.h"
 
@@ -22,11 +18,11 @@
 
 namespace vr_shell {
 
-// An interface for communication with Vr Browser. Many of the functions in this
-// interface are proxies to methods on VrShell.
-class VrBrowserInterface {
+// An interface for the GL thread to communicate with VrShell. Many of the
+// functions in this interface are proxies to methods on VrShell.
+class GlBrowserInterface {
  public:
-  virtual ~VrBrowserInterface() = default;
+  virtual ~GlBrowserInterface() = default;
 
   virtual void ContentSurfaceChanged(jobject surface) = 0;
   virtual void GvrDelegateReady() = 0;
@@ -36,18 +32,13 @@
   virtual void ProcessContentGesture(
       std::unique_ptr<blink::WebInputEvent> event) = 0;
   virtual void ForceExitVr() = 0;
-  virtual void ExitPresent() = 0;
-  virtual void ExitFullscreen() = 0;
   virtual void RunVRDisplayInfoCallback(
       const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback,
       device::mojom::VRDisplayInfoPtr* info) = 0;
   virtual void OnContentPaused(bool enabled) = 0;
-  virtual void NavigateBack() = 0;
-  virtual void ExitCct() = 0;
   virtual void ToggleCardboardGamepad(bool enabled) = 0;
-  virtual void OnUnsupportedMode(UiUnsupportedMode mode) = 0;
 };
 
 }  // namespace vr_shell
 
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_VR_BROWSER_INTERFACE_H_
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_GL_BROWSER_INTERFACE_H_
diff --git a/chrome/browser/android/vr_shell/ui_browser_interface.h b/chrome/browser/android/vr_shell/ui_browser_interface.h
new file mode 100644
index 0000000..d0456055
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_browser_interface.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_BROWSER_INTERFACE_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_BROWSER_INTERFACE_H_
+
+#include "chrome/browser/android/vr_shell/ui_unsupported_mode.h"
+
+namespace vr_shell {
+
+// An interface for the UI to communicate with VrShell.  Many of the functions
+// in this interface are proxies to methods on VrShell.
+class UiBrowserInterface {
+ public:
+  virtual ~UiBrowserInterface() = default;
+
+  virtual void ExitPresent() = 0;
+  virtual void ExitFullscreen() = 0;
+  virtual void NavigateBack() = 0;
+  virtual void ExitCct() = 0;
+  virtual void OnUnsupportedMode(UiUnsupportedMode mode) = 0;
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_BROWSER_INTERFACE_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc b/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
index 00a583f..a511cd5 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
@@ -46,10 +46,10 @@
 TEST(UiElements, AnimateSize) {
   UiElement rect;
   rect.set_size({10, 100, 1});
-  std::unique_ptr<Animation> animation(
-      new Animation(0, Animation::Property::SIZE,
-                    std::unique_ptr<easing::Easing>(new easing::Linear()), {},
-                    {20, 200}, usToTicks(50000), usToDelta(10000)));
+  std::unique_ptr<Animation> animation(new Animation(
+      0, Animation::Property::SIZE,
+      std::unique_ptr<easing::Easing>(new easing::Linear()),
+      std::vector<float>(), {20, 200}, usToTicks(50000), usToDelta(10000)));
   rect.animations().emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
   EXPECT_VEC3F_EQ(gfx::Vector3dF(10, 100, 1), rect.size());
@@ -62,8 +62,9 @@
   rect.set_translation({10, 100, 1000});
   std::unique_ptr<Animation> animation(
       new Animation(0, Animation::Property::TRANSLATION,
-                    std::unique_ptr<easing::Easing>(new easing::Linear()), {},
-                    {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
+                    std::unique_ptr<easing::Easing>(new easing::Linear()),
+                    std::vector<float>(), {20, 200, 2000}, usToTicks(50000),
+                    usToDelta(10000)));
   rect.animations().emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
   EXPECT_VEC3F_EQ(gfx::Vector3dF(10, 100, 1000), rect.translation());
@@ -74,10 +75,11 @@
 TEST(UiElements, AnimateRotation) {
   UiElement rect;
   rect.set_rotation({10, 100, 1000, 10000});
-  std::unique_ptr<Animation> animation(new Animation(
-      0, Animation::Property::ROTATION,
-      std::unique_ptr<easing::Easing>(new easing::Linear()), {},
-      {20, 200, 2000, 20000}, usToTicks(50000), usToDelta(10000)));
+  std::unique_ptr<Animation> animation(
+      new Animation(0, Animation::Property::ROTATION,
+                    std::unique_ptr<easing::Easing>(new easing::Linear()),
+                    std::vector<float>(), {20, 200, 2000, 20000},
+                    usToTicks(50000), usToDelta(10000)));
   rect.animations().emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
   EXPECT_ROTATION(vr::RotationAxisAngle({10, 100, 1000, 10000}),
@@ -146,12 +148,14 @@
   UiElement rect;
   std::unique_ptr<Animation> animation(
       new Animation(0, Animation::Property::TRANSLATION,
-                    std::unique_ptr<easing::Easing>(new easing::Linear()), {},
-                    {20, 200, 2000}, usToTicks(50000), usToDelta(10000)));
+                    std::unique_ptr<easing::Easing>(new easing::Linear()),
+                    std::vector<float>(), {20, 200, 2000}, usToTicks(50000),
+                    usToDelta(10000)));
   std::unique_ptr<Animation> animation2(
       new Animation(0, Animation::Property::TRANSLATION,
-                    std::unique_ptr<easing::Easing>(new easing::Linear()), {},
-                    {50, 500, 5000}, usToTicks(55000), usToDelta(10000)));
+                    std::unique_ptr<easing::Easing>(new easing::Linear()),
+                    std::vector<float>(), {50, 500, 5000}, usToTicks(55000),
+                    usToDelta(10000)));
   rect.animations().emplace_back(std::move(animation));
   rect.animations().emplace_back(std::move(animation2));
   rect.Animate(usToTicks(55000));
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index 2ef910a..4285e81 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/android/vr_shell/textures/close_button_texture.h"
 #include "chrome/browser/android/vr_shell/textures/ui_texture.h"
+#include "chrome/browser/android/vr_shell/ui_browser_interface.h"
 #include "chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/button.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_warning.h"
@@ -21,7 +22,6 @@
 #include "chrome/browser/android/vr_shell/ui_elements/url_bar.h"
 #include "chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_scene.h"
-#include "chrome/browser/android/vr_shell/vr_browser_interface.h"
 
 namespace vr_shell {
 
@@ -75,7 +75,7 @@
 
 }  // namespace
 
-UiSceneManager::UiSceneManager(VrBrowserInterface* browser,
+UiSceneManager::UiSceneManager(UiBrowserInterface* browser,
                                UiScene* scene,
                                bool in_cct,
                                bool in_web_vr)
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h
index ba4ddd8..46802af 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.h
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -17,14 +17,14 @@
 namespace vr_shell {
 
 class LoadingIndicator;
+class UiBrowserInterface;
 class UiElement;
 class UiScene;
 class UrlBar;
-class VrBrowserInterface;
 
 class UiSceneManager {
  public:
-  UiSceneManager(VrBrowserInterface* browser,
+  UiSceneManager(UiBrowserInterface* browser,
                  UiScene* scene,
                  bool in_cct,
                  bool in_web_vr);
@@ -72,7 +72,7 @@
   ColorScheme::Mode mode() const;
   const ColorScheme& color_scheme() const;
 
-  VrBrowserInterface* browser_;
+  UiBrowserInterface* browser_;
   UiScene* scene_;
 
   // UI element pointers (not owned by the scene manager).
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
index 0e1345f..d17d0e8 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
@@ -5,11 +5,12 @@
 #include "chrome/browser/android/vr_shell/ui_scene_manager.h"
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/test/scoped_task_environment.h"
+#include "chrome/browser/android/vr_shell/ui_browser_interface.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h"
 #include "chrome/browser/android/vr_shell/ui_scene.h"
-#include "chrome/browser/android/vr_shell/vr_browser_interface.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -19,35 +20,16 @@
 
 namespace {
 
-class MockBrowserInterface : public VrBrowserInterface {
+class MockBrowserInterface : public UiBrowserInterface {
  public:
   MockBrowserInterface() {}
   ~MockBrowserInterface() override {}
 
-  MOCK_METHOD1(ContentSurfaceChanged, void(jobject));
-  MOCK_METHOD0(GvrDelegateReady, void());
-  MOCK_METHOD1(UpdateGamepadData, void(device::GvrGamepadData));
-  MOCK_METHOD1(AppButtonGesturePerformed, void(UiInterface::Direction));
-
-  MOCK_METHOD0(AppButtonClicked, void());
-  MOCK_METHOD0(ForceExitVr, void());
   MOCK_METHOD0(ExitPresent, void());
   MOCK_METHOD0(ExitFullscreen, void());
-  MOCK_METHOD2(
-      RunVRDisplayInfoCallback,
-      void(const base::Callback<void(device::mojom::VRDisplayInfoPtr)>&,
-           device::mojom::VRDisplayInfoPtr*));
-  MOCK_METHOD1(OnContentPaused, void(bool));
   MOCK_METHOD0(NavigateBack, void());
-  MOCK_METHOD1(SetVideoCapturingIndicator, void(bool));
-  MOCK_METHOD1(SetScreenCapturingIndicator, void(bool));
-  MOCK_METHOD1(SetAudioCapturingIndicator, void(bool));
   MOCK_METHOD0(ExitCct, void());
-  MOCK_METHOD1(ToggleCardboardGamepad, void(bool));
-  MOCK_METHOD1(OnUnsupportedMode, void(UiUnsupportedMode));
-
-  // Stub this as scoped pointers don't work as mock method parameters.
-  void ProcessContentGesture(std::unique_ptr<blink::WebInputEvent>) {}
+  MOCK_METHOD1(OnUnsupportedMode, void(UiUnsupportedMode mode));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockBrowserInterface);
diff --git a/chrome/browser/android/vr_shell/ui_scene_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
index 1f5f81d..2a14566 100644
--- a/chrome/browser/android/vr_shell/ui_scene_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
@@ -48,7 +48,7 @@
                   int animation_id,
                   Animation::Property property) {
   std::unique_ptr<easing::Easing> easing = base::MakeUnique<easing::Linear>();
-  std::vector<float> from = {};
+  std::vector<float> from;
   std::vector<float> to = {1, 1, 1, 1};
   auto animation =
       base::MakeUnique<Animation>(animation_id, property, std::move(easing),
diff --git a/chrome/browser/android/vr_shell/vr_core_info.cc b/chrome/browser/android/vr_shell/vr_core_info.cc
new file mode 100644
index 0000000..6f8b491
--- /dev/null
+++ b/chrome/browser/android/vr_shell/vr_core_info.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/vr_shell/vr_core_info.h"
+
+#include "base/android/jni_android.h"
+#include "jni/VrCoreInfo_jni.h"
+
+using base::android::JavaParamRef;
+
+namespace vr_shell {
+
+VrCoreInfo::VrCoreInfo(int32_t major_version,
+                       int32_t minor_version,
+                       int32_t patch_version,
+                       VrCoreCompatibility compatibility)
+    : gvr_sdk_version({major_version, minor_version, patch_version}),
+      compatibility(compatibility) {}
+
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+
+bool RegisterVrCoreInfo(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+jlong Init(JNIEnv* env,
+           const JavaParamRef<jobject>& obj,
+           jint major_version,
+           jint minor_version,
+           jint patch_version,
+           jint compatibility) {
+  return reinterpret_cast<intptr_t>(
+      new VrCoreInfo(major_version, minor_version, patch_version,
+                     static_cast<VrCoreCompatibility>(compatibility)));
+}
+
+}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_core_info.h b/chrome/browser/android/vr_shell/vr_core_info.h
new file mode 100644
index 0000000..b52d4b9
--- /dev/null
+++ b/chrome/browser/android/vr_shell/vr_core_info.h
@@ -0,0 +1,38 @@
+// 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_ANDROID_VR_SHELL_VR_CORE_INFO_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_VR_CORE_INFO_H_
+
+#include <jni.h>
+
+#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+
+namespace vr_shell {
+
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.vr_shell
+enum VrCoreCompatibility {
+  VR_CORE_COMPATIBILITY_VR_NOT_SUPPORTED = 0,
+  VR_CORE_COMPATIBILITY_VR_NOT_AVAILABLE = 1,
+  VR_CORE_COMPATIBILITY_VR_OUT_OF_DATE = 2,
+  VR_CORE_COMPATIBILITY_VR_READY = 3,
+
+  VR_CORE_COMPATIBILITY_LAST = VR_CORE_COMPATIBILITY_VR_READY,
+};
+
+struct VrCoreInfo {
+  const gvr_version gvr_sdk_version;
+  const VrCoreCompatibility compatibility;
+
+  VrCoreInfo(int32_t major_version,
+             int32_t minor_version,
+             int32_t patch_version,
+             VrCoreCompatibility compatibility);
+};
+
+bool RegisterVrCoreInfo(JNIEnv* env);
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_VR_CORE_INFO_H_
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.h b/chrome/browser/android/vr_shell/vr_gl_thread.h
index 41c83d6..2821694f 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.h
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.h
@@ -11,8 +11,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
+#include "chrome/browser/android/vr_shell/gl_browser_interface.h"
+#include "chrome/browser/android/vr_shell/ui_browser_interface.h"
 #include "chrome/browser/android/vr_shell/ui_interface.h"
-#include "chrome/browser/android/vr_shell/vr_browser_interface.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 
 class GURL;
@@ -25,8 +26,9 @@
 class VrShellGl;
 
 class VrGLThread : public base::Thread,
-                   public UiInterface,
-                   public VrBrowserInterface {
+                   public GlBrowserInterface,
+                   public UiBrowserInterface,
+                   public UiInterface {
  public:
   VrGLThread(
       const base::WeakPtr<VrShell>& weak_vr_shell,
@@ -43,7 +45,7 @@
     return weak_scene_manager_;
   }
 
-  // VrBrowserInterface implementation (VrShellGl calling to UI and VrShell).
+  // GlBrowserInterface implementation (VrShellGl calling to VrShell).
   void ContentSurfaceChanged(jobject surface) override;
   void GvrDelegateReady() override;
   void UpdateGamepadData(device::GvrGamepadData) override;
@@ -52,18 +54,20 @@
   void ProcessContentGesture(
       std::unique_ptr<blink::WebInputEvent> event) override;
   void ForceExitVr() override;
-  void ExitPresent() override;
-  void ExitFullscreen() override;
   void RunVRDisplayInfoCallback(
       const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback,
       device::mojom::VRDisplayInfoPtr* info) override;
   void OnContentPaused(bool enabled) override;
+  void ToggleCardboardGamepad(bool enabled) override;
+
+  // UiBrowserInterface implementation (UI calling to VrShell).
+  void ExitPresent() override;
+  void ExitFullscreen() override;
   void NavigateBack() override;
   void ExitCct() override;
-  void ToggleCardboardGamepad(bool enabled) override;
   void OnUnsupportedMode(UiUnsupportedMode mode) override;
 
-  // UiInterface implementation (VrShell calling to the UI).
+  // UiInterface implementation (VrShell and GL calling to the UI).
   void SetFullscreen(bool enabled) override;
   void SetIncognito(bool incognito) override;
   void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward) override;
diff --git a/chrome/browser/android/vr_shell/vr_metrics_util.cc b/chrome/browser/android/vr_shell/vr_metrics_util.cc
new file mode 100644
index 0000000..5ec7c9b
--- /dev/null
+++ b/chrome/browser/android/vr_shell/vr_metrics_util.cc
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/vr_shell/vr_metrics_util.h"
+
+#include "base/metrics/histogram_macros.h"
+
+static constexpr int kVersionEncodingError = -4;
+static constexpr int kVrNotSupported = -3;
+static constexpr int kGvrNotInstalled = -2;
+static constexpr int kGvrTooOld = -1;
+
+namespace vr_shell {
+
+bool VrMetricsUtil::has_logged_vr_runtime_version_ = false;
+
+void VrMetricsUtil::LogGvrVersionForVrViewerType(
+    gvr_context* context,
+    const VrCoreInfo& vr_core_info) {
+  if (has_logged_vr_runtime_version_) {
+    return;
+  }
+
+  uint32_t encoded_version = kVersionEncodingError;
+  switch (vr_core_info.compatibility) {
+    case VrCoreCompatibility::VR_CORE_COMPATIBILITY_VR_NOT_SUPPORTED:
+      encoded_version = kVrNotSupported;
+      break;
+    case VrCoreCompatibility::VR_CORE_COMPATIBILITY_VR_NOT_AVAILABLE:
+      encoded_version = kGvrNotInstalled;
+      break;
+    case VrCoreCompatibility::VR_CORE_COMPATIBILITY_VR_OUT_OF_DATE:
+      if (vr_core_info.gvr_sdk_version.major == 0 &&
+          vr_core_info.gvr_sdk_version.minor == 0 &&
+          vr_core_info.gvr_sdk_version.patch == 0) {
+        encoded_version = kGvrTooOld;
+        break;
+      }
+    // Fall through since a version can be logged in this case.
+    case VrCoreCompatibility::VR_CORE_COMPATIBILITY_VR_READY:
+      encoded_version =
+          std::min(vr_core_info.gvr_sdk_version.major, 999) * 1000 * 1000 +
+          std::min(vr_core_info.gvr_sdk_version.minor, 999) * 1000 +
+          std::min(vr_core_info.gvr_sdk_version.patch, 999);
+      break;
+  }
+
+  ViewerType vr_viewer_type =
+      context ? GetVrViewerType(context) : ViewerType::UNKNOWN_TYPE;
+  switch (vr_viewer_type) {
+    case ViewerType::CARDBOARD:
+      UMA_HISTOGRAM_SPARSE_SLOWLY("VRRuntimeVersion.GVR.Cardboard",
+                                  encoded_version);
+      break;
+    case ViewerType::DAYDREAM:
+      UMA_HISTOGRAM_SPARSE_SLOWLY("VRRuntimeVersion.GVR.Daydream",
+                                  encoded_version);
+      break;
+    default:
+      UMA_HISTOGRAM_SPARSE_SLOWLY("VRRuntimeVersion.GVR.Unknown",
+                                  encoded_version);
+      break;
+  }
+
+  has_logged_vr_runtime_version_ = true;
+}
+
+void VrMetricsUtil::LogVrViewerType(gvr_context* context) {
+  UMA_HISTOGRAM_ENUMERATION("VRViewerType",
+                            static_cast<int>(GetVrViewerType(context)),
+                            static_cast<int>(ViewerType::VIEWER_TYPE_MAX));
+}
+
+ViewerType VrMetricsUtil::GetVrViewerType(gvr_context* context) {
+  auto gvr_api = gvr::GvrApi::WrapNonOwned(context);
+  switch (gvr_api->GetViewerType()) {
+    case gvr::ViewerType::GVR_VIEWER_TYPE_DAYDREAM:
+      return ViewerType::DAYDREAM;
+    case gvr::ViewerType::GVR_VIEWER_TYPE_CARDBOARD:
+      return ViewerType::CARDBOARD;
+    default:
+      NOTREACHED();
+      return ViewerType::UNKNOWN_TYPE;
+  }
+}
+
+}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_metrics_util.h b/chrome/browser/android/vr_shell/vr_metrics_util.h
new file mode 100644
index 0000000..3075b91
--- /dev/null
+++ b/chrome/browser/android/vr_shell/vr_metrics_util.h
@@ -0,0 +1,39 @@
+// 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_ANDROID_VR_SHELL_VR_METRICS_UTIL_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_VR_METRICS_UTIL_H_
+
+#include "base/macros.h"
+
+#include "chrome/browser/android/vr_shell/vr_core_info.h"
+#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
+#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
+
+namespace vr_shell {
+
+enum class ViewerType {
+  UNKNOWN_TYPE = 0,
+  CARDBOARD = 1,
+  DAYDREAM = 2,
+  VIEWER_TYPE_MAX,
+};
+
+class VrMetricsUtil {
+ public:
+  static void LogGvrVersionForVrViewerType(gvr_context* context,
+                                           const VrCoreInfo& vr_core_info);
+  static void LogVrViewerType(gvr_context* context);
+
+ private:
+  static ViewerType GetVrViewerType(gvr_context* context);
+
+  static bool has_logged_vr_runtime_version_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(VrMetricsUtil);
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_VR_METRICS_UTIL_H_
diff --git a/chrome/browser/android/vr_shell/vr_shell_delegate.cc b/chrome/browser/android/vr_shell/vr_shell_delegate.cc
index bdc7c7d..733f906 100644
--- a/chrome/browser/android/vr_shell/vr_shell_delegate.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_delegate.cc
@@ -9,6 +9,7 @@
 #include "base/android/jni_android.h"
 #include "base/callback_helpers.h"
 #include "chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h"
+#include "chrome/browser/android/vr_shell/vr_metrics_util.h"
 #include "device/vr/android/gvr/gvr_delegate.h"
 #include "device/vr/android/gvr/gvr_device.h"
 #include "device/vr/android/gvr/gvr_device_provider.h"
@@ -16,6 +17,7 @@
 
 using base::android::JavaParamRef;
 using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
 
 namespace vr_shell {
 
@@ -38,8 +40,7 @@
 
 device::GvrDelegateProvider* VrShellDelegate::CreateVrShellDelegate() {
   JNIEnv* env = AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jobject> jdelegate =
-      Java_VrShellDelegate_getInstance(env);
+  ScopedJavaLocalRef<jobject> jdelegate = Java_VrShellDelegate_getInstance(env);
   if (!jdelegate.is_null())
     return GetNativeVrShellDelegate(env, jdelegate.obj());
   return nullptr;
@@ -56,9 +57,9 @@
     gvr_context* context) {
   presenting_delegate_ = delegate;
   // Clean up the non-presenting delegate.
+  JNIEnv* env = AttachCurrentThread();
   if (presenting_delegate_ && non_presenting_delegate_) {
     non_presenting_delegate_ = nullptr;
-    JNIEnv* env = AttachCurrentThread();
     Java_VrShellDelegate_shutdownNonPresentingNativeContext(
         env, j_vr_shell_delegate_.obj());
   }
@@ -74,6 +75,9 @@
     base::ResetAndReturn(&present_callback_).Run(true);
     pending_successful_present_request_ = false;
   }
+
+  std::unique_ptr<VrCoreInfo> vr_core_info = MakeVrCoreInfo(env);
+  VrMetricsUtil::LogGvrVersionForVrViewerType(context, *vr_core_info);
 }
 
 void VrShellDelegate::RemoveDelegate() {
@@ -205,6 +209,11 @@
   }
 }
 
+std::unique_ptr<VrCoreInfo> VrShellDelegate::MakeVrCoreInfo(JNIEnv* env) {
+  return base::WrapUnique(reinterpret_cast<VrCoreInfo*>(
+      Java_VrShellDelegate_getVrCoreInfo(env, j_vr_shell_delegate_.obj())));
+}
+
 void VrShellDelegate::CreateNonPresentingDelegate() {
   JNIEnv* env = AttachCurrentThread();
   gvr_context* context = reinterpret_cast<gvr_context*>(
@@ -214,6 +223,8 @@
       base::MakeUnique<NonPresentingGvrDelegate>(context);
   non_presenting_delegate_->UpdateVSyncInterval(timebase_nanos_,
                                                 interval_seconds_);
+  std::unique_ptr<VrCoreInfo> vr_core_info = MakeVrCoreInfo(env);
+  VrMetricsUtil::LogGvrVersionForVrViewerType(context, *vr_core_info);
 }
 
 void VrShellDelegate::OnActivateDisplayHandled(bool will_not_present) {
diff --git a/chrome/browser/android/vr_shell/vr_shell_delegate.h b/chrome/browser/android/vr_shell/vr_shell_delegate.h
index 71147d2..8265e347 100644
--- a/chrome/browser/android/vr_shell/vr_shell_delegate.h
+++ b/chrome/browser/android/vr_shell/vr_shell_delegate.h
@@ -12,6 +12,8 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/callback.h"
 #include "base/macros.h"
+#include "chrome/browser/android/vr_shell/vr_core_info.h"
+#include "chrome/browser/android/vr_shell/vr_usage_monitor.h"
 #include "device/vr/android/gvr/gvr_delegate_provider.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 
@@ -69,11 +71,11 @@
                            const base::Callback<void(bool)>& callback) override;
   device::GvrDelegate* GetDelegate() override;
   void SetListeningForActivate(bool listening) override;
-
   void CreateNonPresentingDelegate();
-
   void OnActivateDisplayHandled(bool will_not_present);
 
+  std::unique_ptr<VrCoreInfo> MakeVrCoreInfo(JNIEnv* env);
+
   std::unique_ptr<NonPresentingGvrDelegate> non_presenting_delegate_;
   base::android::ScopedJavaGlobalRef<jobject> j_vr_shell_delegate_;
   device::GvrDeviceProvider* device_provider_ = nullptr;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index deb62e5..731f651 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -15,15 +15,17 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/android/vr_shell/fps_meter.h"
+#include "chrome/browser/android/vr_shell/gl_browser_interface.h"
 #include "chrome/browser/android/vr_shell/mailbox_to_surface_bridge.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
 #include "chrome/browser/android/vr_shell/ui_interface.h"
 #include "chrome/browser/android/vr_shell/ui_scene.h"
-#include "chrome/browser/android/vr_shell/vr_browser_interface.h"
 #include "chrome/browser/android/vr_shell/vr_controller.h"
 #include "chrome/browser/android/vr_shell/vr_gl_util.h"
+#include "chrome/browser/android/vr_shell/vr_metrics_util.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_renderer.h"
+#include "chrome/browser/android/vr_shell/vr_usage_monitor.h"
 #include "device/vr/android/gvr/gvr_delegate.h"
 #include "device/vr/android/gvr/gvr_device.h"
 #include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
@@ -171,12 +173,6 @@
   return mouse_event;
 }
 
-enum class ViewerType {
-  UNKNOWN_TYPE = 0,
-  CARDBOARD = 1,
-  DAYDREAM = 2,
-  VIEWER_TYPE_MAX,
-};
 
 void MatfToGvrMat(const vr::Mat4f& in, gvr::Mat4f* out) {
   // If our std::array implementation doesn't have any non-data members, we can
@@ -221,7 +217,7 @@
 
 }  // namespace
 
-VrShellGl::VrShellGl(VrBrowserInterface* browser,
+VrShellGl::VrShellGl(GlBrowserInterface* browser,
                      gvr_context* gvr_api,
                      bool initially_web_vr,
                      bool reprojected_rendering,
@@ -435,23 +431,10 @@
   gvr_api_ = gvr::GvrApi::WrapNonOwned(gvr_api);
   controller_.reset(new VrController(gvr_api));
 
-  ViewerType viewerType;
-  switch (gvr_api_->GetViewerType()) {
-    case gvr::ViewerType::GVR_VIEWER_TYPE_DAYDREAM:
-      viewerType = ViewerType::DAYDREAM;
-      break;
-    case gvr::ViewerType::GVR_VIEWER_TYPE_CARDBOARD:
-      viewerType = ViewerType::CARDBOARD;
-      break;
-    default:
-      NOTREACHED();
-      viewerType = ViewerType::UNKNOWN_TYPE;
-      break;
-  }
-  UMA_HISTOGRAM_ENUMERATION("VRViewerType", static_cast<int>(viewerType),
-                            static_cast<int>(ViewerType::VIEWER_TYPE_MAX));
+  VrMetricsUtil::LogVrViewerType(gvr_api);
 
-  cardboard_ = (viewerType == ViewerType::CARDBOARD);
+  cardboard_ =
+      (gvr_api_->GetViewerType() == gvr::ViewerType::GVR_VIEWER_TYPE_CARDBOARD);
   if (cardboard_ && web_vr_mode_) {
     browser_->ToggleCardboardGamepad(true);
   }
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index f88836e..d16925a 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -47,7 +47,7 @@
 class SlidingAverage;
 class UiElement;
 class UiScene;
-class VrBrowserInterface;
+class GlBrowserInterface;
 class VrController;
 class VrShell;
 class VrShellRenderer;
@@ -66,7 +66,7 @@
 // It is not threadsafe and must only be used on the GL thread.
 class VrShellGl : public device::mojom::VRVSyncProvider {
  public:
-  VrShellGl(VrBrowserInterface* browser,
+  VrShellGl(GlBrowserInterface* browser,
             gvr_context* gvr_api,
             bool initially_web_vr,
             bool reprojected_rendering,
@@ -266,7 +266,7 @@
   mojo::Binding<device::mojom::VRVSyncProvider> binding_;
   device::mojom::VRSubmitFrameClientPtr submit_client_;
 
-  VrBrowserInterface* browser_;
+  GlBrowserInterface* browser_;
 
   UiScene* scene_ = nullptr;
 
diff --git a/chrome/browser/chrome_test_browser_overlay.json b/chrome/browser/chrome_test_browser_overlay.json
new file mode 100644
index 0000000..3f9b14f
--- /dev/null
+++ b/chrome/browser/chrome_test_browser_overlay.json
@@ -0,0 +1,11 @@
+{
+  "name": "content_browser",
+  "display_name": "Chrome",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "requires": {
+        "ui": [ "test" ]
+      }
+    }
+  }
+}
diff --git a/chrome/browser/component_updater/cros_component_installer.cc b/chrome/browser/component_updater/cros_component_installer.cc
index f2ed8cf..a0e39018 100644
--- a/chrome/browser/component_updater/cros_component_installer.cc
+++ b/chrome/browser/component_updater/cros_component_installer.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/component_updater/cros_component_installer.h"
+
+#include <utility>
+
 #include "base/task_scheduler/post_task.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/component_installer_errors.h"
@@ -11,11 +14,16 @@
 #include "content/public/browser/browser_thread.h"
 
 #if defined(OS_CHROMEOS)
-#include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/image_loader_client.h"
 #endif  // defined(OS_CHROMEOS)
 
+#define CONFIG_MAP_CONTENT        \
+  {{"epson-inkjet-printer-escpr", \
+    {{"env_version", "2.1"},      \
+     {"sha2hashstr",              \
+      "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"}}}};
+
 using content::BrowserThread;
 
 namespace component_updater {
@@ -81,7 +89,6 @@
 CrOSComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
     const base::FilePath& install_dir) {
-  DVLOG(1) << "[CrOSComponentInstallerTraits::OnCustomInstall]";
   std::string version;
   if (!manifest.GetString("version", &version)) {
     return ToInstallerResult(update_client::InstallError::GENERIC_ERROR);
@@ -96,7 +103,12 @@
     const base::Version& version,
     const base::FilePath& path,
     std::unique_ptr<base::DictionaryValue> manifest) {
-  g_browser_process->platform_part()->AddCompatibleCrOSComponent(GetName());
+  std::string min_env_version;
+  if (manifest && manifest->GetString("min_env_version", &min_env_version)) {
+    if (IsCompatible(env_version, min_env_version)) {
+      g_browser_process->platform_part()->AddCompatibleCrOSComponent(GetName());
+    }
+  }
 }
 
 bool CrOSComponentInstallerTraits::VerifyInstallation(
@@ -129,89 +141,108 @@
   return mime_types;
 }
 
-void CrOSComponent::RegisterCrOSComponentInternal(
-    ComponentUpdateService* cus,
-    const ComponentConfig& config,
-    const base::Closure& installcallback) {
-  std::unique_ptr<ComponentInstallerTraits> traits(
-      new CrOSComponentInstallerTraits(config));
-  // |cus| will take ownership of |installer| during
-  // installer->Register(cus).
-  DefaultComponentInstaller* installer =
-      new DefaultComponentInstaller(std::move(traits));
-  installer->Register(cus, installcallback);
+bool CrOSComponentInstallerTraits::IsCompatible(
+    const std::string& env_version_str,
+    const std::string& min_env_version_str) {
+  base::Version env_version(env_version_str);
+  base::Version min_env_version(min_env_version_str);
+  return env_version.IsValid() && min_env_version.IsValid() &&
+         env_version.components()[0] == min_env_version.components()[0] &&
+         env_version >= min_env_version;
 }
 
-void CrOSComponent::InstallChromeOSComponent(
+// It returns load result passing the following as parameters in
+// load_callback: call_status - dbus call status, result - component mount
+// point.
+static void LoadResult(
+    const base::Callback<void(const std::string&)>& load_callback,
+    chromeos::DBusMethodCallStatus call_status,
+    const std::string& result) {
+  PostTask(
+      FROM_HERE,
+      base::Bind(
+          load_callback,
+          call_status != chromeos::DBUS_METHOD_CALL_SUCCESS ? "" : result));
+}
+
+// Internal function to load a component.
+static void LoadComponentInternal(
+    const std::string& name,
+    const base::Callback<void(const std::string&)>& load_callback) {
+  DCHECK(g_browser_process->platform_part()->IsCompatibleCrOSComponent(name));
+  chromeos::ImageLoaderClient* loader =
+      chromeos::DBusThreadManager::Get()->GetImageLoaderClient();
+  if (loader) {
+    loader->LoadComponent(name, base::Bind(&LoadResult, load_callback));
+  } else {
+    base::PostTask(FROM_HERE, base::Bind(load_callback, ""));
+  }
+}
+
+// It calls LoadComponentInternal to load the installed component.
+static void InstallResult(
+    const std::string& name,
+    const base::Callback<void(const std::string&)>& load_callback,
+    update_client::Error error) {
+  LoadComponentInternal(name, load_callback);
+}
+
+// It calls OnDemandUpdate to install the component right after being
+// registered.
+void CrOSComponent::RegisterResult(
     ComponentUpdateService* cus,
     const std::string& id,
     const update_client::Callback& install_callback) {
   cus->GetOnDemandUpdater().OnDemandUpdate(id, install_callback);
 }
 
-bool CrOSComponent::InstallCrOSComponent(
+// Register a component with a dedicated ComponentUpdateService instance.
+static void RegisterComponent(ComponentUpdateService* cus,
+                              const ComponentConfig& config,
+                              const base::Closure& register_callback) {
+  std::unique_ptr<ComponentInstallerTraits> traits(
+      new CrOSComponentInstallerTraits(config));
+  // |cus| will take ownership of |installer| during
+  // installer->Register(cus).
+  DefaultComponentInstaller* installer =
+      new DefaultComponentInstaller(std::move(traits));
+  installer->Register(cus, register_callback);
+}
+
+// Install a component with a dedicated ComponentUpdateService instance.
+void CrOSComponent::InstallComponent(
+    ComponentUpdateService* cus,
     const std::string& name,
-    const update_client::Callback& install_callback) {
-  auto* const cus = g_browser_process->component_updater();
-  const ConfigMap components = {
-      {"epson-inkjet-printer-escpr",
-       {{"env_version", "0.0"},
-        {"sha2hashstr",
-         "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"}}}};
-  if (name.empty()) {
-    base::PostTask(
-        FROM_HERE,
-        base::Bind(install_callback, update_client::Error::INVALID_ARGUMENT));
-    return false;
-  }
+    const base::Callback<void(const std::string&)>& load_callback) {
+  const ConfigMap components = CONFIG_MAP_CONTENT;
   const auto it = components.find(name);
-  if (it == components.end()) {
-    DVLOG(1) << "[RegisterCrOSComponents] component " << name
-             << " is not in configuration.";
-    base::PostTask(
-        FROM_HERE,
-        base::Bind(install_callback, update_client::Error::INVALID_ARGUMENT));
-    return false;
+  if (name.empty() || it == components.end()) {
+    base::PostTask(FROM_HERE, base::Bind(load_callback, ""));
+    return;
   }
   ComponentConfig config(it->first, it->second.find("env_version")->second,
                          it->second.find("sha2hashstr")->second);
-  RegisterCrOSComponentInternal(
-      cus, config,
-      base::Bind(InstallChromeOSComponent, cus,
-                 crx_file::id_util::GenerateIdFromHex(
-                     it->second.find("sha2hashstr")->second)
-                     .substr(0, 32),
-                 install_callback));
-  return true;
+  RegisterComponent(cus, config,
+                    base::Bind(RegisterResult, cus,
+                               crx_file::id_util::GenerateIdFromHex(
+                                   it->second.find("sha2hashstr")->second)
+                                   .substr(0, 32),
+                               base::Bind(InstallResult, name, load_callback)));
 }
 
-void MountResult(const base::Callback<void(const std::string&)>& mount_callback,
-                 chromeos::DBusMethodCallStatus call_status,
-                 const std::string& result) {
-  if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
-    DVLOG(1) << "Call to imageloader service failed.";
-    base::PostTask(FROM_HERE, base::Bind(mount_callback, ""));
-    return;
-  }
-  if (result.empty()) {
-    DVLOG(1) << "Component load failed";
-    base::PostTask(FROM_HERE, base::Bind(mount_callback, ""));
-    return;
-  }
-  base::PostTask(FROM_HERE, base::Bind(mount_callback, result));
-}
-
-void CrOSComponent::LoadCrOSComponent(
+void CrOSComponent::LoadComponent(
     const std::string& name,
-    const base::Callback<void(const std::string&)>& mount_callback) {
-  chromeos::ImageLoaderClient* loader =
-      chromeos::DBusThreadManager::Get()->GetImageLoaderClient();
-  if (loader) {
-    loader->LoadComponent(name, base::Bind(&MountResult, mount_callback));
+    const base::Callback<void(const std::string&)>& load_callback) {
+  if (!g_browser_process->platform_part()->IsCompatibleCrOSComponent(name)) {
+    // A compatible component is not installed, start installation process.
+    auto* const cus = g_browser_process->component_updater();
+    InstallComponent(cus, name, load_callback);
   } else {
-    DVLOG(1) << "Failed to get ImageLoaderClient object.";
+    // A compatible component is intalled, load it directly.
+    LoadComponentInternal(name, load_callback);
   }
 }
+
 #endif  // defined(OS_CHROMEOS
 
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/cros_component_installer.h b/chrome/browser/component_updater/cros_component_installer.h
index 7673d74..ad07fbb8 100644
--- a/chrome/browser/component_updater/cros_component_installer.h
+++ b/chrome/browser/component_updater/cros_component_installer.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_INSTALLER_H_
 #define CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_INSTALLER_H_
 
+#include <map>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "build/build_config.h"
 #include "components/component_updater/component_updater_service.h"
@@ -13,6 +16,24 @@
 #include "components/update_client/update_client.h"
 #include "crypto/sha2.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_method_call_status.h"
+#endif  // defined(OS_CHROMEOS)
+
+//  Developer API usage:
+//  ...
+//  void LoadCallback(const std::string& mount_point){
+//    if (mount_point.empty()) {
+//      // component is not loaded.
+//      return;
+//    }
+//    ...
+//  }
+//  ...
+//  component_updater::CrOSComponent::LoadComponent(
+//            name,
+//            base::Bind(&LoadCallback));
+//
 namespace component_updater {
 
 #if defined(OS_CHROMEOS)
@@ -34,6 +55,11 @@
   ~CrOSComponentInstallerTraits() override {}
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, IsCompatibleOrNot);
+  FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest,
+                           ComponentReadyCorrectManifest);
+  FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest,
+                           ComponentReadyWrongManifest);
   // The following methods override ComponentInstallerTraits.
   bool SupportsGroupPolicyEnabledComponentUpdates() const override;
   bool RequiresNetworkEncryption() const override;
@@ -50,6 +76,9 @@
   std::string GetName() const override;
   update_client::InstallerAttributes GetInstallerAttributes() const override;
   std::vector<std::string> GetMimeTypes() const override;
+
+  virtual bool IsCompatible(const std::string& env_version_str,
+                            const std::string& min_env_version_str);
   std::string name;
   std::string env_version;
   uint8_t kSha2Hash_[crypto::kSHA256Length] = {};
@@ -60,62 +89,22 @@
 // This class contains functions used to register and install a component.
 class CrOSComponent {
  public:
-  //  Register and start installing a CrOS component.
-  //  |install_callback| is triggered after install finishes and returns error
-  //  code.
-  //
-  //  example:
-  //  ...
-  //  void load_callback(const std::string& result){
-  //    if (result.empty) {
-  //      // component is not mounted.
-  //      return;
-  //    }
-  //    // [component mount point: result]
-  //  }
-  //  void install_callback(update_client::Error error){
-  //    // switch(error){
-  //    //   case update_client::Error::NONE:
-  //    //     // component is installed
-  //    //     break;
-  //    //   case update_client::Error::INVALID_ARGUMENT:
-  //    //     // your install failed due to your wrong parameters.
-  //    //     break;
-  //    //   default:
-  //    //     // your install failed due to system failure.
-  //    //     break;
-  //    // }
-  //    // Even when error code other than NONE returned (your install failed),
-  //    // component might has already being installed previously.
-  //    // Plus, if you want to know current version of component.
-  //    component_updater:CrOSComponent::LoadCrOSComponent(name, load_callback);
-  //  }
-  //  ...
-  //    component_updater::CrOSComponent::InstallCrOSComponent(
-  //              name,
-  //              base::Bind(&install_callback));
-  //
-  static bool InstallCrOSComponent(
+  static void LoadComponent(
       const std::string& name,
-      const update_client::Callback& install_callback);
-
-  static void LoadCrOSComponent(
-      const std::string& name,
-      const base::Callback<void(const std::string&)>& mount_callback);
+      const base::Callback<void(const std::string&)>& load_callback);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest,
+                           RegisterComponentSuccess);
+  FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, RegisterComponentFail);
   CrOSComponent() {}
-  // Register a component.
-  static void RegisterCrOSComponentInternal(ComponentUpdateService* cus,
-                                            const ComponentConfig& config,
-                                            const base::Closure& callback);
-  // A helper function to pass into RegisterCrOSComonentInternal as a callback.
-  // It calls OnDemandUpdate to install the component right after being
-  // registered.
-  static void InstallChromeOSComponent(
+  static void RegisterResult(ComponentUpdateService* cus,
+                             const std::string& id,
+                             const update_client::Callback& install_callback);
+  static void InstallComponent(
       ComponentUpdateService* cus,
-      const std::string& id,
-      const update_client::Callback& install_callback);
+      const std::string& name,
+      const base::Callback<void(const std::string&)>& load_callback);
 };
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/component_updater/cros_component_installer_unittest.cc b/chrome/browser/component_updater/cros_component_installer_unittest.cc
index b723d52..e2cbb96 100644
--- a/chrome/browser/component_updater/cros_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/cros_component_installer_unittest.cc
@@ -40,51 +40,16 @@
   DISALLOW_COPY_AND_ASSIGN(CrOSComponentInstallerTest);
 };
 
-class FakeInstallerTraits : public ComponentInstallerTraits {
+class MockCrOSComponentInstallerTraits : public CrOSComponentInstallerTraits {
  public:
-  ~FakeInstallerTraits() override {}
-
-  bool VerifyInstallation(const base::DictionaryValue& manifest,
-                          const base::FilePath& dir) const override {
-    return true;
-  }
-
-  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
-    return true;
-  }
-
-  bool RequiresNetworkEncryption() const override { return true; }
-
-  update_client::CrxInstaller::Result OnCustomInstall(
-      const base::DictionaryValue& manifest,
-      const base::FilePath& install_dir) override {
-    return update_client::CrxInstaller::Result(0);
-  }
-
-  void ComponentReady(
-      const base::Version& version,
-      const base::FilePath& install_dir,
-      std::unique_ptr<base::DictionaryValue> manifest) override {}
-
-  base::FilePath GetRelativeInstallDir() const override {
-    return base::FilePath(FILE_PATH_LITERAL("fake"));
-  }
-
-  void GetHash(std::vector<uint8_t>* hash) const override {}
-
-  std::string GetName() const override { return "fake name"; }
-
-  update_client::InstallerAttributes GetInstallerAttributes() const override {
-    update_client::InstallerAttributes installer_attributes;
-    return installer_attributes;
-  }
-
-  std::vector<std::string> GetMimeTypes() const override {
-    return std::vector<std::string>();
-  }
+  explicit MockCrOSComponentInstallerTraits(const ComponentConfig& config)
+      : CrOSComponentInstallerTraits(config) {}
+  MOCK_METHOD2(IsCompatible,
+               bool(const std::string& env_version_str,
+                    const std::string& min_env_version_str));
 };
 
-void install_callback(update_client::Error error) {}
+void load_callback(const std::string& result) {}
 
 TEST_F(CrOSComponentInstallerTest, BPPPCompatibleCrOSComponent) {
   BrowserProcessPlatformPart bppp;
@@ -93,13 +58,57 @@
   ASSERT_EQ(bppp.IsCompatibleCrOSComponent("a"), true);
 }
 
-TEST_F(CrOSComponentInstallerTest, RegisterComponentFail) {
-  std::unique_ptr<CrOSMockComponentUpdateService> cus =
-      base::MakeUnique<CrOSMockComponentUpdateService>();
-  EXPECT_CALL(*cus, RegisterComponent(testing::_)).Times(0);
-  component_updater::CrOSComponent::InstallCrOSComponent(
-      "a-component-not-exist", base::Bind(install_callback));
+TEST_F(CrOSComponentInstallerTest, RegisterComponentSuccess) {
+  CrOSMockComponentUpdateService cus;
+  EXPECT_CALL(cus, RegisterComponent(testing::_)).Times(1);
+  component_updater::CrOSComponent::InstallComponent(
+      &cus, "epson-inkjet-printer-escpr", base::Bind(load_callback));
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(CrOSComponentInstallerTest, RegisterComponentFail) {
+  CrOSMockComponentUpdateService cus;
+  EXPECT_CALL(cus, RegisterComponent(testing::_)).Times(0);
+  component_updater::CrOSComponent::InstallComponent(
+      &cus, "a-component-not-exist", base::Bind(load_callback));
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(CrOSComponentInstallerTest, ComponentReadyCorrectManifest) {
+  ComponentConfig config("a", "2.1", "");
+  MockCrOSComponentInstallerTraits traits(config);
+  EXPECT_CALL(traits, IsCompatible(testing::_, testing::_)).Times(1);
+  base::Version version;
+  base::FilePath path;
+  std::unique_ptr<base::DictionaryValue> manifest =
+      base::MakeUnique<base::DictionaryValue>();
+  manifest->SetString("min_env_version", "2.1");
+  traits.ComponentReady(version, path, std::move(manifest));
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(CrOSComponentInstallerTest, ComponentReadyWrongManifest) {
+  ComponentConfig config("a", "2.1", "");
+  MockCrOSComponentInstallerTraits traits(config);
+  EXPECT_CALL(traits, IsCompatible(testing::_, testing::_)).Times(0);
+  base::Version version;
+  base::FilePath path;
+  std::unique_ptr<base::DictionaryValue> manifest =
+      base::MakeUnique<base::DictionaryValue>();
+  traits.ComponentReady(version, path, std::move(manifest));
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(CrOSComponentInstallerTest, IsCompatibleOrNot) {
+  ComponentConfig config("", "", "");
+  CrOSComponentInstallerTraits traits(config);
+  EXPECT_TRUE(traits.IsCompatible("1.0", "1.0"));
+  EXPECT_TRUE(traits.IsCompatible("1.1", "1.0"));
+  EXPECT_FALSE(traits.IsCompatible("1.0", "1.1"));
+  EXPECT_FALSE(traits.IsCompatible("1.0", "2.0"));
+  EXPECT_FALSE(traits.IsCompatible("1.c", "1.c"));
+  EXPECT_FALSE(traits.IsCompatible("1", "1.1"));
+  EXPECT_TRUE(traits.IsCompatible("1.1.1", "1.1"));
+}
+
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/pepper_flash_component_installer.cc b/chrome/browser/component_updater/pepper_flash_component_installer.cc
index 9f79c136..f15ed8dc 100644
--- a/chrome/browser/component_updater/pepper_flash_component_installer.cc
+++ b/chrome/browser/component_updater/pepper_flash_component_installer.cc
@@ -52,7 +52,6 @@
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/image_loader_client.h"
-#include "content/public/browser/browser_thread.h"
 #elif defined(OS_LINUX)
 #include "chrome/common/component_flash_hint_file_linux.h"
 #endif  // defined(OS_CHROMEOS)
@@ -114,39 +113,39 @@
 
 // Determine whether or not to skip registering flash component updates.
 bool SkipFlashRegistration(ComponentUpdateService* cus) {
-   if (!base::FeatureList::IsEnabled(features::kCrosCompUpdates))
-     return true;
+  if (!base::FeatureList::IsEnabled(features::kCrosCompUpdates))
+    return true;
 
-   // If the version of Chrome is pinned on the device (probably via enterprise
-   // policy), do not component update Flash player.
-   chromeos::CrosSettingsProvider::TrustedStatus status =
-       chromeos::CrosSettings::Get()->PrepareTrustedValues(
-           base::Bind(&RegisterPepperFlashComponent, cus));
+  // If the version of Chrome is pinned on the device (probably via enterprise
+  // policy), do not component update Flash player.
+  chromeos::CrosSettingsProvider::TrustedStatus status =
+      chromeos::CrosSettings::Get()->PrepareTrustedValues(
+          base::Bind(&RegisterPepperFlashComponent, cus));
 
-   // Only if the settings are trusted, read the update settings and allow them
-   // to disable Flash component updates. If the settings are untrusted, then we
-   // fail-safe and allow the security updates.
-   std::string version_prefix;
-   bool update_disabled = false;
-   switch (status) {
-     case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
-       // Return and allow flash registration to occur once the settings are
-       // trusted.
-       return true;
-     case chromeos::CrosSettingsProvider::TRUSTED:
-       chromeos::CrosSettings::Get()->GetBoolean(chromeos::kUpdateDisabled,
-                                                 &update_disabled);
-       chromeos::CrosSettings::Get()->GetString(chromeos::kTargetVersionPrefix,
-                                                &version_prefix);
+  // Only if the settings are trusted, read the update settings and allow them
+  // to disable Flash component updates. If the settings are untrusted, then we
+  // fail-safe and allow the security updates.
+  std::string version_prefix;
+  bool update_disabled = false;
+  switch (status) {
+    case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
+      // Return and allow flash registration to occur once the settings are
+      // trusted.
+      return true;
+    case chromeos::CrosSettingsProvider::TRUSTED:
+      chromeos::CrosSettings::Get()->GetBoolean(chromeos::kUpdateDisabled,
+                                                &update_disabled);
+      chromeos::CrosSettings::Get()->GetString(chromeos::kTargetVersionPrefix,
+                                               &version_prefix);
 
-       return update_disabled || !version_prefix.empty();
-     case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
-       return false;
-   }
+      return update_disabled || !version_prefix.empty();
+    case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
+      return false;
+  }
 
-   // Default to not skipping component flash registration since updates are
-   // security critical.
-   return false;
+  // Default to not skipping component flash registration since updates are
+  // security critical.
+  return false;
 }
 #endif  // defined(OS_CHROMEOS)
 #endif  // defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/browser/component_updater/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl_component_installer.cc
index 1256f95..bd0206a 100644
--- a/chrome/browser/component_updater/pnacl_component_installer.cc
+++ b/chrome/browser/component_updater/pnacl_component_installer.cc
@@ -231,11 +231,11 @@
 }
 
 update_client::CrxInstaller::Result PnaclComponentInstaller::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
   return update_client::InstallFunctionWrapper(
       base::Bind(&PnaclComponentInstaller::DoInstall, base::Unretained(this),
-                 base::ConstRef(manifest), base::ConstRef(unpack_path)));
+                 base::ConstRef(*manifest), base::ConstRef(unpack_path)));
 }
 
 bool PnaclComponentInstaller::DoInstall(const base::DictionaryValue& manifest,
diff --git a/chrome/browser/component_updater/pnacl_component_installer.h b/chrome/browser/component_updater/pnacl_component_installer.h
index 3f3981c3..f9af6ddd 100644
--- a/chrome/browser/component_updater/pnacl_component_installer.h
+++ b/chrome/browser/component_updater/pnacl_component_installer.h
@@ -44,7 +44,7 @@
   // ComponentInstaller implementation:
   void OnUpdateError(int error) override;
   update_client::CrxInstaller::Result Install(
-      const base::DictionaryValue& manifest,
+      std::unique_ptr<base::DictionaryValue> manifest,
       const base::FilePath& unpack_path) override;
   bool GetInstalledFile(const std::string& file,
                         base::FilePath* installed_file) override;
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index 1bd9bfbd..eca672a 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/base_paths.h"
@@ -259,7 +260,7 @@
   void OnUpdateError(int error) override;
 
   update_client::CrxInstaller::Result Install(
-      const base::DictionaryValue& manifest,
+      std::unique_ptr<base::DictionaryValue> manifest,
       const base::FilePath& unpack_path) override;
 
   bool GetInstalledFile(const std::string& file,
@@ -396,11 +397,11 @@
 #endif  // defined(OS_POSIX)
 
 update_client::CrxInstaller::Result RecoveryComponentInstaller::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
-  return update_client::InstallFunctionWrapper(
-      base::Bind(&RecoveryComponentInstaller::DoInstall, base::Unretained(this),
-                 base::ConstRef(manifest), base::ConstRef(unpack_path)));
+  return update_client::InstallFunctionWrapper(base::Bind(
+      &RecoveryComponentInstaller::DoInstall, base::Unretained(this),
+      base::ConstRef(*manifest), base::ConstRef(unpack_path)));
 }
 
 bool RecoveryComponentInstaller::DoInstall(
diff --git a/chrome/browser/component_updater/ssl_error_assistant_component_installer.cc b/chrome/browser/component_updater/ssl_error_assistant_component_installer.cc
index 7fd3ebe0..4ed156f 100644
--- a/chrome/browser/component_updater/ssl_error_assistant_component_installer.cc
+++ b/chrome/browser/component_updater/ssl_error_assistant_component_installer.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/component_updater/ssl_error_assistant_component_installer.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
diff --git a/chrome/browser/component_updater/ssl_error_assistant_component_installer.h b/chrome/browser/component_updater/ssl_error_assistant_component_installer.h
index 88cfd07f..8d6fbee2 100644
--- a/chrome/browser/component_updater/ssl_error_assistant_component_installer.h
+++ b/chrome/browser/component_updater/ssl_error_assistant_component_installer.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_COMPONENT_UPDATER_SSL_ERROR_ASSISTANT_COMPONENT_INSTALLER_H_
 #define CHROME_BROWSER_COMPONENT_UPDATER_SSL_ERROR_ASSISTANT_COMPONENT_INSTALLER_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc
index 2ac3278..67ae2e0 100644
--- a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc
+++ b/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
@@ -202,7 +203,8 @@
   SupervisedUserWhitelistInstallerTest()
       : testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
         user_data_dir_override_(chrome::DIR_USER_DATA),
-        component_update_service_(base::ThreadTaskRunnerHandle::Get()) {}
+        component_update_service_(base::ThreadTaskRunnerHandle::Get()),
+        manifest_(base::MakeUnique<base::DictionaryValue>()) {}
 
   ~SupervisedUserWhitelistInstallerTest() override {}
 
@@ -238,15 +240,15 @@
     std::unique_ptr<base::DictionaryValue> whitelist_dict(
         new base::DictionaryValue);
     whitelist_dict->SetString("sites", kWhitelistFile);
-    manifest_.Set("whitelisted_content", std::move(whitelist_dict));
+    manifest_->Set("whitelisted_content", std::move(whitelist_dict));
 
     large_icon_path_ = whitelist_version_directory_.AppendASCII(kLargeIconFile);
     std::unique_ptr<base::DictionaryValue> icons_dict(
         new base::DictionaryValue);
     icons_dict->SetString("128", kLargeIconFile);
-    manifest_.Set("icons", std::move(icons_dict));
+    manifest_->Set("icons", std::move(icons_dict));
 
-    manifest_.SetString("version", kVersion);
+    manifest_->SetString("version", kVersion);
 
     std::unique_ptr<base::DictionaryValue> crx_dict(new base::DictionaryValue);
     crx_dict->SetString("name", kName);
@@ -277,7 +279,7 @@
     PrepareWhitelistFile(whitelist_directory.AppendASCII(kWhitelistFile));
     base::FilePath manifest_file =
         whitelist_directory.AppendASCII("manifest.json");
-    ASSERT_TRUE(JSONFileValueSerializer(manifest_file).Serialize(manifest_));
+    ASSERT_TRUE(JSONFileValueSerializer(manifest_file).Serialize(*manifest_));
   }
 
   void RegisterExistingComponents() {
@@ -308,7 +310,7 @@
   base::FilePath installed_whitelist_directory_;
   base::FilePath whitelist_path_;
   base::FilePath large_icon_path_;
-  base::DictionaryValue manifest_;
+  std::unique_ptr<base::DictionaryValue> manifest_;
   base::DictionaryValue pref_;
 };
 
@@ -353,7 +355,8 @@
   const CrxComponent* component =
       component_update_service_.registered_component();
   ASSERT_TRUE(component);
-  const auto result = component->installer->Install(manifest_, unpacked_path);
+  const auto result =
+      component->installer->Install(std::move(manifest_), unpacked_path);
   EXPECT_EQ(0, result.error);
   EXPECT_EQ(0, result.extended_error);
 
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index 3406ec30..151e914 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -268,7 +268,6 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kIgnoreAutoplayRestrictionsForTests);
-    command_line->AppendSwitch(switches::kEnableVp9InMp4);
   }
 
 #if BUILDFLAG(ENABLE_PEPPER_CDMS)
diff --git a/chrome/browser/media/encrypted_media_supported_types_browsertest.cc b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
index 9e2a8abe..05e066d 100644
--- a/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
@@ -161,7 +161,6 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     InProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableVp9InMp4);
     command_line->AppendSwitch(switches::kEnableNewVp9CodecString);
   }
 
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index 690a3c34..ec85bdf1 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -25,14 +25,18 @@
 // Controls if browser side Cast device discovery is enabled.
 const base::Feature kEnableCastDiscovery{"EnableCastDiscovery",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Controls if local media casting is enabled.
+const base::Feature kEnableCastLocalMedia{"EnableCastLocalMedia",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
 #if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
 namespace {
 const PrefService::Preference* GetMediaRouterPref(
     content::BrowserContext* context) {
-  return user_prefs::UserPrefs::Get(context)
-      ->FindPreference(prefs::kEnableMediaRouter);
+  return user_prefs::UserPrefs::Get(context)->FindPreference(
+      prefs::kEnableMediaRouter);
 }
 }  // namespace
 #endif  // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
@@ -62,6 +66,11 @@
 bool CastDiscoveryEnabled() {
   return base::FeatureList::IsEnabled(kEnableCastDiscovery);
 }
+
+// Returns true if local media casting is enabled.
+bool CastLocalMediaEnabled() {
+  return base::FeatureList::IsEnabled(kEnableCastLocalMedia);
+}
 #endif
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index d4bc4b9..bd493c9 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -20,6 +20,9 @@
 
 // Returns true if browser side Cast discovery is enabled.
 bool CastDiscoveryEnabled();
+
+// Returns true if local media casting is enabled.
+bool CastLocalMediaEnabled();
 #endif
 
 }  // namespace media_router
diff --git a/chrome/browser/net/crl_set_fetcher.cc b/chrome/browser/net/crl_set_fetcher.cc
index 1a2bfba..1fba045 100644
--- a/chrome/browser/net/crl_set_fetcher.cc
+++ b/chrome/browser/net/crl_set_fetcher.cc
@@ -166,11 +166,11 @@
 }
 
 update_client::CrxInstaller::Result CRLSetFetcher::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
   const auto result = update_client::InstallFunctionWrapper(
       base::Bind(&CRLSetFetcher::DoInstall, base::Unretained(this),
-                 base::ConstRef(manifest), base::ConstRef(unpack_path)));
+                 base::ConstRef(*manifest), base::ConstRef(unpack_path)));
   base::DeleteFile(unpack_path, true);
   return result;
 }
diff --git a/chrome/browser/net/crl_set_fetcher.h b/chrome/browser/net/crl_set_fetcher.h
index c045aafe..45965d1 100644
--- a/chrome/browser/net/crl_set_fetcher.h
+++ b/chrome/browser/net/crl_set_fetcher.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/compiler_specific.h"
@@ -41,7 +42,7 @@
   // ComponentInstaller interface
   void OnUpdateError(int error) override;
   update_client::CrxInstaller::Result Install(
-      const base::DictionaryValue& manifest,
+      std::unique_ptr<base::DictionaryValue> manifest,
       const base::FilePath& unpack_path) override;
   bool GetInstalledFile(const std::string& file,
                         base::FilePath* installed_file) override;
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index cfcd519..056deee 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -3869,10 +3869,17 @@
    public:
     MockInstaller() {}
 
+    // gMock does not support mocking functions with parameters which have
+    // move semantics. This function is a shim to work around it.
+    Result Install(std::unique_ptr<base::DictionaryValue> manifest,
+                   const base::FilePath& unpack_path) {
+      return Install_(manifest, unpack_path);
+    }
+
     MOCK_METHOD1(OnUpdateError, void(int error));
-    MOCK_METHOD2(Install,
+    MOCK_METHOD2(Install_,
                  update_client::CrxInstaller::Result(
-                     const base::DictionaryValue& manifest,
+                     const std::unique_ptr<base::DictionaryValue>& manifest,
                      const base::FilePath& unpack_path));
     MOCK_METHOD2(GetInstalledFile,
                  bool(const std::string& file, base::FilePath* installed_file));
diff --git a/chrome/browser/printing/print_job_manager.h b/chrome/browser/printing/print_job_manager.h
index e4970d4..4b17058 100644
--- a/chrome/browser/printing/print_job_manager.h
+++ b/chrome/browser/printing/print_job_manager.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/non_thread_safe.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index f0effd2..0069cf49 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -132,6 +132,7 @@
 #if defined(OS_POSIX) && !defined(OS_ANDROID)
   static void DisablePromptForTesting();
   static void SkipIsChromeProcessCheckForTesting(bool skip);
+  static void SetUserOptedUnlockInUseProfileForTesting(bool set_unlock);
 #endif
 #if defined(OS_WIN)
   // Called to query whether to kill a hung browser process that has visible
@@ -184,11 +185,15 @@
   bool IsSameChromeInstance(pid_t pid);
 
   // Extract the process's pid from a symbol link path and if it is on
-  // the same host, kill the process, unlink the lock file and return true.
+  // the same host or is_connected_to_socket is true, kill the process, unlink
+  // the lock file and return true.
   // If the process is part of the same chrome instance, unlink the lock file
   // and return true without killing it.
-  // If the process is on a different host, return false.
-  bool KillProcessByLockPath();
+  // If the process is on a different host and is_connected_to_socket is false,
+  // display profile in use error dialog (on Linux). If user opted to unlock
+  // profile (on Mac OS X by default), unlink the lock file and return true.
+  // Otherwise return false.
+  bool KillProcessByLockPath(bool is_connected_to_socket);
 
   // Default function to kill a process, overridable by tests.
   void KillProcess(int pid);
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 1962d655..b8df920 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -122,6 +122,7 @@
 
 bool g_disable_prompt = false;
 bool g_skip_is_chrome_process_check = false;
+bool g_user_opted_unlock_in_use_profile = false;
 
 // Set the close-on-exec bit on a file descriptor.
 // Returns 0 on success, -1 on failure.
@@ -322,7 +323,7 @@
   LOG(ERROR) << error;
 
   if (g_disable_prompt)
-    return false;
+    return g_user_opted_unlock_in_use_profile;
 
 #if defined(OS_LINUX)
   base::string16 relaunch_button_text = l10n_util::GetStringUTF16(
@@ -338,11 +339,13 @@
 }
 
 bool IsChromeProcess(pid_t pid) {
+  if (g_skip_is_chrome_process_check)
+    return true;
+
   base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
   return (!other_chrome_path.empty() &&
-          (g_skip_is_chrome_process_check ||
-           other_chrome_path.BaseName() ==
-               base::FilePath(chrome::kBrowserProcessExecutableName)));
+          other_chrome_path.BaseName() ==
+              base::FilePath(chrome::kBrowserProcessExecutableName));
 }
 
 // A helper class to hold onto a socket.
@@ -826,7 +829,7 @@
 
     if (retries == retry_attempts) {
       // Retries failed.  Kill the unresponsive chrome process and continue.
-      if (!kill_unresponsive || !KillProcessByLockPath())
+      if (!kill_unresponsive || !KillProcessByLockPath(false))
         return PROFILE_IN_USE;
       SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED);
       return PROCESS_NONE;
@@ -862,7 +865,7 @@
   // Send the message
   if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
     // Try to kill the other process, because it might have been dead.
-    if (!kill_unresponsive || !KillProcessByLockPath())
+    if (!kill_unresponsive || !KillProcessByLockPath(true))
       return PROFILE_IN_USE;
     SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED);
     return PROCESS_NONE;
@@ -878,7 +881,7 @@
 
   // Failed to read ACK, the other process might have been frozen.
   if (len <= 0) {
-    if (!kill_unresponsive || !KillProcessByLockPath())
+    if (!kill_unresponsive || !KillProcessByLockPath(true))
       return PROFILE_IN_USE;
     SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED);
     return PROCESS_NONE;
@@ -967,14 +970,22 @@
   kill_callback_ = callback;
 }
 
+// static
 void ProcessSingleton::DisablePromptForTesting() {
   g_disable_prompt = true;
 }
 
+// static
 void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) {
   g_skip_is_chrome_process_check = skip;
 }
 
+// static
+void ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(
+    bool set_unlock) {
+  g_user_opted_unlock_in_use_profile = set_unlock;
+}
+
 bool ProcessSingleton::Create() {
   int sock;
   sockaddr_un addr;
@@ -1081,15 +1092,18 @@
   return true;
 }
 
-bool ProcessSingleton::KillProcessByLockPath() {
+bool ProcessSingleton::KillProcessByLockPath(bool is_connected_to_socket) {
   std::string hostname;
   int pid;
   ParseLockPath(lock_path_, &hostname, &pid);
 
-  if (!hostname.empty() && hostname != net::GetHostName()) {
+  if (!hostname.empty() && hostname != net::GetHostName() &&
+      !is_connected_to_socket) {
     bool res = DisplayProfileInUseError(lock_path_, hostname, pid);
-    if (res)
+    if (res) {
+      UnlinkPath(lock_path_);
       SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL);
+    }
     return res;
   }
   UnlinkPath(lock_path_);
diff --git a/chrome/browser/process_singleton_posix_unittest.cc b/chrome/browser/process_singleton_posix_unittest.cc
index 9b09212..005cd38d 100644
--- a/chrome/browser/process_singleton_posix_unittest.cc
+++ b/chrome/browser/process_singleton_posix_unittest.cc
@@ -83,6 +83,7 @@
 
     ProcessSingleton::DisablePromptForTesting();
     ProcessSingleton::SkipIsChromeProcessCheckForTesting(false);
+    ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(false);
     // Put the lock in a temporary directory.  Doesn't need to be a
     // full profile to test this code.
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -338,9 +339,10 @@
   CheckNotified();
 }
 
-// Test that we fail when lock says process is on another host and we can't
-// notify it over the socket.
+// Test that we kill hung browser when lock says process is on another host and
+// we can't notify it over the socket.
 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessDifferingHost) {
+  base::HistogramTester histogram_tester;
   CreateProcessSingletonOnThread();
 
   BlockWorkerThread();
@@ -348,16 +350,27 @@
   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
 
-  EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcess(false));
+  EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
+  ASSERT_EQ(1, kill_callbacks_);
 
-  ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
+  // lock_path_ should be unlinked in NotifyOtherProcess().
+  base::FilePath target_path;
+  EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
 
   UnblockWorkerThread();
+
+  histogram_tester.ExpectUniqueSample(
+      "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
+      ProcessSingleton::SOCKET_READ_FAILED, 1u);
 }
 
-// Test that we fail when lock says process is on another host and we can't
-// notify it over the socket.
-TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_DifferingHost) {
+// Test that we'll start creating ProcessSingleton when we have old lock file
+// that says process is on another host and there is browser with the same pid
+// but with another user data dir. Also suppose that user opted to unlock
+// profile.
+TEST_F(ProcessSingletonPosixTest,
+       NotifyOtherProcessDifferingHost_UnlockedProfileBeforeKill) {
+  base::HistogramTester histogram_tester;
   CreateProcessSingletonOnThread();
 
   BlockWorkerThread();
@@ -365,12 +378,56 @@
   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
 
+  // Remove socket so that we will not be able to notify the existing browser.
+  EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
+
+  // Unlock profile that was locked by process on another host.
+  ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
+  // Treat process with pid 1234 as browser with different user data dir.
+  ProcessSingleton::SkipIsChromeProcessCheckForTesting(true);
+
+  EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
+
+  // lock_path_ should be unlinked in NotifyOtherProcess().
+  base::FilePath target_path;
+  EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
+
+  UnblockWorkerThread();
+
+  histogram_tester.ExpectUniqueSample(
+      "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
+      ProcessSingleton::NOTIFY_ATTEMPTS_EXCEEDED, 1u);
+  histogram_tester.ExpectUniqueSample(
+      "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
+      ProcessSingleton::PROFILE_UNLOCKED_BEFORE_KILL, 1u);
+}
+
+// Test that we unlock profile when lock says process is on another host and we
+// can't notify it over the socket.
+TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_DifferingHost) {
+  base::HistogramTester histogram_tester;
+  CreateProcessSingletonOnThread();
+
+  BlockWorkerThread();
+
+  EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
+  EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
+
+  // Remove socket so that we will not be able to notify the existing browser.
+  EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
+  // Unlock profile that was locked by process on another host.
+  ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
+
   std::string url("about:blank");
-  EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcessOrCreate(url));
+  EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcessOrCreate(url));
 
   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
 
   UnblockWorkerThread();
+
+  histogram_tester.ExpectUniqueSample(
+      "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
+      ProcessSingleton::PROFILE_UNLOCKED, 1u);
 }
 
 // Test that Create fails when another browser is using the profile directory.
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
index b1ae7f9..c6939f6 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
@@ -170,7 +170,7 @@
   width: 30px;
 }
 
-#share-screen-text {
+.subheading-text {
   -webkit-padding-start: var(--dialog-padding-start);
   color: var(--paper-grey-600);
   cursor: default;
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
index b6af91a..e946623 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
@@ -68,12 +68,25 @@
               <div><span>[[item.host]]</span></div>
             </paper-item>
           </template>
-          <div id="share-screen-text"
+          <div id="share-screen-text" class="subheading-text"
               hidden$="[[computeShareScreenSubheadingHidden_(castModeList)]]">
             <span>[[i18n('shareYourScreenSubheadingText')]]</span>
           </div>
-          <template is="dom-repeat" id="nonPresentationCastModeList"
-              items="[[computeNonPresentationCastModeList_(castModeList)]]">
+          <template is="dom-repeat" id="shareScreenCastModeList"
+              items="[[computeShareScreenCastModeList_(castModeList)]]">
+            <paper-item on-tap="onCastModeClick_">
+              <iron-icon class="cast-mode-icon"
+                  icon="[[computeCastModeIcon_(item)]]">
+              </iron-icon>
+              <div><span>[[item.description]]</span></div>
+            </paper-item>
+          </template>
+          <div id="cast-local-media-text" class="subheading-text"
+              hidden$="[[computeLocalMediaSubheadingHidden_(castModeList)]]">
+            <span>[[i18n('castLocalMediaSubheadingText')]]</span>
+          </div>
+          <template is="dom-repeat" id="localMediaCastModeList"
+              items="[[computeLocalMediaCastModeList_(castModeList)]]">
             <paper-item on-tap="onCastModeClick_">
               <iron-icon class="cast-mode-icon"
                   icon="[[computeCastModeIcon_(item)]]">
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
index 4163d78..f7c8325 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
@@ -680,6 +680,8 @@
         return 'media-router:tab';
       case media_router.CastModeType.DESKTOP_MIRROR:
         return 'media-router:laptop';
+      case media_router.CastModeType.LOCAL_FILE:
+        return 'media-router:folder';
       default:
         return '';
     }
@@ -812,12 +814,26 @@
    * @param {!Array<!media_router.CastMode>} castModeList The current list of
    *     cast modes.
    * @return {!Array<!media_router.CastMode>} The list of non-PRESENTATION cast
+   *     modes. Also excludes LOCAL_FILE.
+   * @private
+   */
+  computeShareScreenCastModeList_: function(castModeList) {
+    return castModeList.filter(function(mode) {
+      return mode.type == media_router.CastModeType.DESKTOP_MIRROR ||
+          mode.type == media_router.CastModeType.TAB_MIRROR;
+    });
+  },
+
+  /**
+   * @param {!Array<!media_router.CastMode>} castModeList The current list of
+   *     cast modes.
+   * @return {!Array<!media_router.CastMode>} The list of local media cast
    *     modes.
    * @private
    */
-  computeNonPresentationCastModeList_: function(castModeList) {
+  computeLocalMediaCastModeList_: function(castModeList) {
     return castModeList.filter(function(mode) {
-      return mode.type != media_router.CastModeType.PRESENTATION;
+      return mode.type == media_router.CastModeType.LOCAL_FILE;
     });
   },
 
@@ -889,7 +905,17 @@
    * @private
    */
   computeShareScreenSubheadingHidden_: function(castModeList) {
-    return this.computeNonPresentationCastModeList_(castModeList).length == 0;
+    return this.computeShareScreenCastModeList_(castModeList).length == 0;
+  },
+
+  /**
+   * @param {!Array<!media_router.CastMode>} castModeList The current list of
+   *     cast modes.
+   * @return {boolean} Whether or not to hide the local media subheading text.
+   * @private
+   */
+  computeLocalMediaSubheadingHidden_: function(castModeList) {
+    return this.computeLocalMediaCastModeList_(castModeList).length == 0;
   },
 
   /**
@@ -1597,20 +1623,25 @@
    * @private
    */
   onCastModeClick_: function(event) {
-    // The clicked cast mode can come from one of two lists,
-    // presentationCastModeList and nonPresentationCastModeList.
+    // The clicked cast mode can come from one of three lists,
+    // presentationCastModeList, shareScreenCastModeList, and
+    // localMediaCastModeList.
     var clickedMode =
         this.$$('#presentationCastModeList').itemForElement(event.target) ||
-        this.$$('#nonPresentationCastModeList').itemForElement(event.target);
+        this.$$('#shareScreenCastModeList').itemForElement(event.target) ||
+        this.$$('#localMediaCastModeList').itemForElement(event.target);
 
     if (!clickedMode)
       return;
 
-    this.selectCastMode(clickedMode.type);
-    this.fire('cast-mode-selected', {castModeType: clickedMode.type});
-    this.showSinkList_();
-    this.maybeReportUserFirstAction(
-        media_router.MediaRouterUserAction.CHANGE_MODE);
+    // If the user selects LOCAL_FILE, some additional steps are required
+    // (selecting the file), before the cast mode has been officially
+    // selected.
+    if (clickedMode.type == media_router.CastModeType.LOCAL_FILE) {
+      this.selectLocalMediaFile_();
+    } else {
+      this.castModeSelected_(clickedMode);
+    }
   },
 
   /**
@@ -1966,6 +1997,47 @@
   },
 
   /**
+   * Sets up the LOCAL_FILE cast mode for display after a specific file has been
+   * selected.
+   *
+   * @param {string} fileName The name of the file that has been selected.
+   */
+  onFileDialogSuccess(fileName) {
+    /** @const */ var mode =
+        this.findCastModeByType_(media_router.CastModeType.LOCAL_FILE);
+
+    if (!mode) {
+      return;
+    }
+
+    this.castModeSelected_(mode);
+    this.headerText =
+        loadTimeData.getStringF('castLocalMediaSelectedFileTitle', fileName);
+  },
+
+  /**
+   * Calls all the functions to set the UI to a given cast mode.
+   * @param {media_router.CastMode} castMode The cast mode to set things to.
+   * @private
+   */
+  castModeSelected_(castMode) {
+    this.selectCastMode(castMode.type);
+    this.fire('cast-mode-selected', {castModeType: castMode.type});
+    this.showSinkList_();
+    this.maybeReportUserFirstAction(
+        media_router.MediaRouterUserAction.CHANGE_MODE);
+  },
+
+  /**
+   * Fires the command to open a file dialog.
+   *
+   * @private
+   */
+  selectLocalMediaFile_() {
+    this.fire('select-local-media-file');
+  },
+
+  /**
    * Called when |routeList| is updated. Rebuilds |routeMap_| and
    * |sinkToRouteMap_|.
    *
@@ -2022,6 +2094,7 @@
     var updatedSinkList = this.allSinks.filter(function(sink) {
       return !sink.isPseudoSink;
     }, this);
+
     if (this.pseudoSinkSearchState_) {
       var pendingPseudoSink = this.pseudoSinkSearchState_.getPseudoSink();
       // Here we will treat the pseudo sink that launched the search as a real
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container_interface.js b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container_interface.js
index 4a449144..9cbfadd 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container_interface.js
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container_interface.js
@@ -124,6 +124,13 @@
     function(sinkId, route, isForDisplay) {};
 
 /**
+ * Handles the result of a requested file dialog.
+ * @param {string} fileName The name of the file that has been selected.
+ */
+MediaRouterContainerInterface.prototype.onFileDialogSuccess = function(
+    fileName) {};
+
+/**
  * Called when a search has completed up to route creation. |sinkId|
  * identifies the sink that should be in |allSinks|, if a sink was found.
  * @param {string} sinkId The ID of the sink that is the result of the
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
index 4d3a65bc..91d86b54c 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
@@ -40,6 +40,15 @@
     },
 
     /**
+     * The timestamp for when the initial media status was loaded.
+     * @private {number}
+     */
+    initialLoadTime_: {
+      type: Number,
+      value: 0,
+    },
+
+    /**
      * Set to true when the user is dragging the seek bar. Updates for the
      * current time from the browser will be ignored when set to true.
      * @private {boolean}
@@ -60,6 +69,15 @@
     },
 
     /**
+     * The timestamp for when the route details view was opened.
+     * @type {number}
+     */
+    routeDetailsOpenTime: {
+      type: Number,
+      value: 0,
+    },
+
+    /**
      * The status of the media route shown.
      * @type {!media_router.RouteStatus}
      */
@@ -172,15 +190,6 @@
   },
 
   /**
-   * Resets the route controls. Called when the route details view is closed.
-   */
-  reset: function() {
-    this.routeStatus = new media_router.RouteStatus(
-        '', '', false, false, false, false, false, false, 0, 0, 0);
-    media_router.ui.setRouteControls(null);
-  },
-
-  /**
    * Updates seek and volume bars if the user is not currently dragging on
    * them.
    * @param {!media_router.RouteStatus} newRouteStatus
@@ -197,6 +206,11 @@
     if (newRouteStatus.description !== '') {
       this.displayedDescription_ = newRouteStatus.description;
     }
+    if (!this.initialLoadTime_) {
+      this.initialLoadTime_ = Date.now();
+      media_router.browserApi.reportWebUIRouteControllerLoaded(
+          this.initialLoadTime_ - this.routeDetailsOpenTime);
+    }
   },
 
   /**
@@ -258,4 +272,13 @@
     var target = /** @type {{immediateValue: number}} */ (e.target);
     this.volumeSliderValue_ = target.immediateValue;
   },
+
+  /**
+   * Resets the route controls. Called when the route details view is closed.
+   */
+  reset: function() {
+    this.routeStatus = new media_router.RouteStatus(
+        '', '', false, false, false, false, false, false, 0, 0, 0);
+    media_router.ui.setRouteControls(null);
+  },
 });
diff --git a/chrome/browser/resources/media_router/elements/route_details/extension_view_wrapper/extension_view_wrapper.js b/chrome/browser/resources/media_router/elements/route_details/extension_view_wrapper/extension_view_wrapper.js
index 997e7e08..5469f52 100644
--- a/chrome/browser/resources/media_router/elements/route_details/extension_view_wrapper/extension_view_wrapper.js
+++ b/chrome/browser/resources/media_router/elements/route_details/extension_view_wrapper/extension_view_wrapper.js
@@ -9,6 +9,16 @@
 
   properties: {
     /**
+     * Whether the extension view is ready to be shown.
+     * @type {boolean}
+     */
+    isExtensionViewReady: {
+      type: Boolean,
+      value: false,
+      notify: true,
+    },
+
+    /**
      * The route to show the custom controller for.
      * @type {?media_router.Route|undefined}
      */
@@ -18,13 +28,12 @@
     },
 
     /**
-     * Whether the extension view is ready to be shown.
-     * @type {boolean}
+     * The timestamp for when the route details view was opened.
+     * @type {number}
      */
-    isExtensionViewReady: {
-      type: Boolean,
-      value: false,
-      notify: true,
+    routeDetailsOpenTime: {
+      type: Number,
+      value: 0,
     },
   },
 
@@ -36,21 +45,32 @@
   },
 
   /**
+   * @return {?string}
+   */
+  getCustomControllerPath_: function() {
+    if (!this.route || !this.route.customControllerPath) {
+      return null;
+    }
+    return this.route.customControllerPath +
+        '&requestTimestamp=' + this.routeDetailsOpenTime;
+  },
+
+  /**
    * Loads the custom controller if the controller path for the current route is
    * valid.
    */
   maybeLoadExtensionView_: function() {
-    var extensionview = this.$['custom-controller'];
+    /** @const */ var extensionview = this.$['custom-controller'];
+    /** @const */ var controllerPath = this.getCustomControllerPath_();
 
     // Do nothing if the controller path doesn't exist or is already shown in
     // the extension view.
-    if (!this.route || !this.route.customControllerPath ||
-        this.route.customControllerPath == extensionview.src) {
+    if (!controllerPath || controllerPath == extensionview.src) {
       return;
     }
 
-    var that = this;
-    extensionview.load(this.route.customControllerPath)
+    /** @const */ var that = this;
+    extensionview.load(controllerPath)
         .then(
             function() {
               // Load was successful; show the custom controller.
diff --git a/chrome/browser/resources/media_router/elements/route_details/route_details.html b/chrome/browser/resources/media_router/elements/route_details/route_details.html
index 1313fb2..606f299f 100644
--- a/chrome/browser/resources/media_router/elements/route_details/route_details.html
+++ b/chrome/browser/resources/media_router/elements/route_details/route_details.html
@@ -13,12 +13,14 @@
     </div>
     <template is="dom-if" if="[[!useWebUiRouteControls]]">
       <extension-view-wrapper id="extension-view-wrapper" route="[[route]]"
+          route-details-open-time="[[openTime_]]"
           is-extension-view-ready="{{isExtensionViewReady}}"
           hidden$="[[!shouldShowExtensionView_(controllerType_)]]">
       </extension-view-wrapper>
     </template>
     <template is="dom-if" if="[[shouldShowWebUiControls_(controllerType_)]]">
-      <route-controls id="route-controls"></route-controls>
+      <route-controls id="route-controls"
+          route-details-open-time="[[openTime_]]"></route-controls>
     </template>
     <div id="route-action-buttons" class="layout">
       <paper-button flat class="route-button button"
diff --git a/chrome/browser/resources/media_router/elements/route_details/route_details.js b/chrome/browser/resources/media_router/elements/route_details/route_details.js
index da76f91..eeaf2a5d 100644
--- a/chrome/browser/resources/media_router/elements/route_details/route_details.js
+++ b/chrome/browser/resources/media_router/elements/route_details/route_details.js
@@ -56,6 +56,19 @@
     },
 
     /**
+     * The timestamp for when the route details view was opened. We initialize
+     * the value in a function so that the value is set when the element is
+     * loaded, rather than at page load.
+     * @private {number}
+     */
+    openTime_: {
+      type: Number,
+      value: function() {
+        return Date.now();
+      },
+    },
+
+    /**
      * The route to show.
      * @type {?media_router.Route|undefined}
      */
diff --git a/chrome/browser/resources/media_router/icons/media_router_icons.html b/chrome/browser/resources/media_router/icons/media_router_icons.html
index aad793c..ff93aca7 100644
--- a/chrome/browser/resources/media_router/icons/media_router_icons.html
+++ b/chrome/browser/resources/media_router/icons/media_router_icons.html
@@ -11,6 +11,7 @@
   <g id="arrow-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></path></g>
   <g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g>
   <g id="error-outline"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g>
+  <g id="folder"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path></g>
   <g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g>
   <g id="tab"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h10v4h8v10z"></path></g>
 
diff --git a/chrome/browser/resources/media_router/media_router.js b/chrome/browser/resources/media_router/media_router.js
index 206f1f4..cd65124 100644
--- a/chrome/browser/resources/media_router/media_router.js
+++ b/chrome/browser/resources/media_router/media_router.js
@@ -46,6 +46,8 @@
                                onNavigateToDetails);
     container.addEventListener('navigate-to-cast-mode-list',
                                onNavigateToCastMode);
+    container.addEventListener(
+        'select-local-media-file', onSelectLocalMediaFile);
     container.addEventListener('report-filter', onFilter);
     container.addEventListener('report-initial-action', onInitialAction);
     container.addEventListener('report-initial-action-close',
@@ -115,6 +117,13 @@
   }
 
   /**
+   * Sends a request to the browser to select a local file.
+   */
+  function onSelectLocalMediaFile() {
+    media_router.browserApi.selectLocalMediaFile();
+  }
+
+  /**
    * Updates the preference that the user has seen the first run flow.
    * Called when the user clicks on the acknowledgement button on the first run
    * flow.
diff --git a/chrome/browser/resources/media_router/media_router_browser_api.js b/chrome/browser/resources/media_router/media_router_browser_api.js
index 482decf..ac132fdb 100644
--- a/chrome/browser/resources/media_router/media_router_browser_api.js
+++ b/chrome/browser/resources/media_router/media_router_browser_api.js
@@ -215,6 +215,16 @@
   }
 
   /**
+   * Reports the time, in ms, it took the WebUI route controller to load media
+   * status info.
+   *
+   * @param {number} timeMs
+   */
+  function reportWebUIRouteControllerLoaded(timeMs) {
+    chrome.send('reportWebUIRouteControllerLoaded', [timeMs]);
+  }
+
+  /**
    * Requests data to initialize the WebUI with.
    * The data will be returned via media_router.ui.setInitialData.
    */
@@ -265,6 +275,10 @@
     chrome.send('seekCurrentMedia', [{time: time}]);
   }
 
+  function selectLocalMediaFile() {
+    chrome.send('selectLocalMediaFile');
+  }
+
   /**
    * Sends a command to mute or unmute the route shown in the route details
    * view.
@@ -309,10 +323,12 @@
     reportSinkCount: reportSinkCount,
     reportTimeToClickSink: reportTimeToClickSink,
     reportTimeToInitialActionClose: reportTimeToInitialActionClose,
+    reportWebUIRouteControllerLoaded: reportWebUIRouteControllerLoaded,
     requestInitialData: requestInitialData,
     requestRoute: requestRoute,
     searchSinksAndCreateRoute: searchSinksAndCreateRoute,
     seekCurrentMedia: seekCurrentMedia,
+    selectLocalMediaFile: selectLocalMediaFile,
     setCurrentMediaMute: setCurrentMediaMute,
     setCurrentMediaVolume: setCurrentMediaVolume,
   };
diff --git a/chrome/browser/resources/media_router/media_router_data.js b/chrome/browser/resources/media_router/media_router_data.js
index 441ea8e..53d5b4ce 100644
--- a/chrome/browser/resources/media_router/media_router_data.js
+++ b/chrome/browser/resources/media_router/media_router_data.js
@@ -21,6 +21,7 @@
   PRESENTATION: 0x1,
   TAB_MIRROR: 0x2,
   DESKTOP_MIRROR: 0x4,
+  LOCAL_FILE: 0x8,
 };
 
 /**
diff --git a/chrome/browser/resources/media_router/media_router_ui_interface.js b/chrome/browser/resources/media_router/media_router_ui_interface.js
index abe9a27..655fc32 100644
--- a/chrome/browser/resources/media_router/media_router_ui_interface.js
+++ b/chrome/browser/resources/media_router/media_router_ui_interface.js
@@ -198,6 +198,10 @@
     }
   }
 
+  function userSelectedLocalMediaFile(fileName) {
+    container.onFileDialogSuccess(fileName);
+  }
+
   return {
     onCreateRouteResponseReceived: onCreateRouteResponseReceived,
     onRouteControllerInvalidated: onRouteControllerInvalidated,
@@ -212,5 +216,6 @@
     setSinkListAndIdentity: setSinkListAndIdentity,
     updateMaxHeight: updateMaxHeight,
     updateRouteStatus: updateRouteStatus,
+    userSelectedLocalMediaFile: userSelectedLocalMediaFile,
   };
 });
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a02cf3e2..55d9c1af 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -646,12 +646,7 @@
     ]
   }
 
-  if (is_android) {
-    sources += [
-      "webui/large_icon_source.cc",
-      "webui/large_icon_source.h",
-    ]
-  } else {
+  if (!is_android) {
     sources += [
       "apps/app_info_dialog.h",
       "apps/chrome_app_delegate.cc",
@@ -3476,6 +3471,8 @@
       "webui/media_router/media_cast_mode.h",
       "webui/media_router/media_router_dialog_controller_impl.cc",
       "webui/media_router/media_router_dialog_controller_impl.h",
+      "webui/media_router/media_router_file_dialog.cc",
+      "webui/media_router/media_router_file_dialog.h",
       "webui/media_router/media_router_localized_strings_provider.cc",
       "webui/media_router/media_router_localized_strings_provider.h",
       "webui/media_router/media_router_resources_provider.cc",
diff --git a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
index b87d780..3df8d855 100644
--- a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
@@ -74,8 +74,7 @@
   // the second mouse event arrives. In this case, |ShowContextMenu()| will
   // get called multiple times - if so, don't create another context menu.
   // TODO(asvitkine): Fix the renderer so that it doesn't do this.
-  content::RenderWidgetHostView* widget_view = GetActiveRenderWidgetHostView();
-  if (widget_view && widget_view->IsShowingContextMenu())
+  if (web_contents_->IsShowingContextMenu())
     return;
 
   context_menu_->Show();
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index b106079..21cfdb2 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -35,7 +35,7 @@
 #include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/views/event_monitor.h"
-#include "ui/views/focus/view_storage.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
 
@@ -192,8 +192,7 @@
       attached_tabstrip_(NULL),
       can_release_capture_(true),
       offset_to_width_ratio_(0),
-      old_focused_view_id_(
-          views::ViewStorage::GetInstance()->CreateStorageID()),
+      old_focused_view_tracker_(base::MakeUnique<views::ViewTracker>()),
       last_move_screen_loc_(0),
       started_drag_(false),
       active_(true),
@@ -220,8 +219,6 @@
 }
 
 TabDragController::~TabDragController() {
-  views::ViewStorage::GetInstance()->RemoveView(old_focused_view_id_);
-
   if (instance_ == this)
     instance_ = NULL;
 
@@ -477,11 +474,8 @@
 
 void TabDragController::SaveFocus() {
   DCHECK(source_tabstrip_);
-  views::View* focused_view =
-      source_tabstrip_->GetFocusManager()->GetFocusedView();
-  if (focused_view)
-    views::ViewStorage::GetInstance()->StoreView(old_focused_view_id_,
-                                                 focused_view);
+  old_focused_view_tracker_->SetView(
+      source_tabstrip_->GetFocusManager()->GetFocusedView());
   source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
   // WARNING: we may have been deleted.
 }
@@ -495,8 +489,7 @@
     }
     return;
   }
-  views::View* old_focused_view =
-      views::ViewStorage::GetInstance()->RetrieveView(old_focused_view_id_);
+  views::View* old_focused_view = old_focused_view_tracker_->view();
   if (!old_focused_view)
     return;
   old_focused_view->GetFocusManager()->SetFocusedView(old_focused_view);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 513542c..1dc420e 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -27,6 +27,7 @@
 }
 namespace views {
 class View;
+class ViewTracker;
 }
 class Browser;
 class Tab;
@@ -493,10 +494,10 @@
   // This is used to calculate |window_create_point_|.
   gfx::Point first_source_tab_point_;
 
-  // Storage ID in ViewStorage where the last view that had focus in the window
-  // containing |source_tab_| is saved. This is saved so that focus can be
-  // restored properly when a drag begins and ends within this same window.
-  const int old_focused_view_id_;
+  // Used to track the view that had focus in the window containing
+  // |source_tab_|. This is saved so that focus can be restored properly when
+  // a drag begins and ends within this same window.
+  std::unique_ptr<views::ViewTracker> old_focused_view_tracker_;
 
   // The horizontal position of the mouse cursor in screen coordinates at the
   // time of the last re-order event.
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 7a33df5..0ed2cb5 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
-#include "chrome/browser/ui/webui/large_icon_source.h"
 #include "chrome/common/features.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
@@ -269,14 +268,7 @@
 
   // Create our favicon data source.
   Profile* profile = Profile::FromWebUI(web_ui());
-
-#if defined(OS_ANDROID)
-  favicon::LargeIconService* large_icon_service =
-      LargeIconServiceFactory::GetForBrowserContext(profile);
-  content::URLDataSource::Add(profile, new LargeIconSource(large_icon_service));
-#else
   content::URLDataSource::Add(profile, new FaviconSource(profile));
-#endif
 
   web_ui()->RegisterMessageCallback("queryHistory",
       base::Bind(&BrowsingHistoryHandler::HandleQueryHistory,
diff --git a/chrome/browser/ui/webui/large_icon_source.cc b/chrome/browser/ui/webui/large_icon_source.cc
deleted file mode 100644
index 165fb78c4..0000000
--- a/chrome/browser/ui/webui/large_icon_source.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/large_icon_source.h"
-
-#include <vector>
-
-#include "base/memory/ref_counted_memory.h"
-#include "chrome/browser/search/instant_io_context.h"
-#include "chrome/common/url_constants.h"
-#include "components/favicon/core/large_icon_service.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "components/favicon_base/favicon_types.h"
-#include "components/favicon_base/large_icon_url_parser.h"
-#include "net/url_request/url_request.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/codec/png_codec.h"
-
-namespace {
-
-const int kMaxLargeIconSize = 192;  // Arbitrary bound to safeguard endpoint.
-
-}  // namespace
-
-LargeIconSource::LargeIconSource(favicon::LargeIconService* large_icon_service)
-    : large_icon_service_(large_icon_service) {}
-
-LargeIconSource::~LargeIconSource() {
-}
-
-std::string LargeIconSource::GetSource() const {
-  return chrome::kChromeUILargeIconHost;
-}
-
-void LargeIconSource::StartDataRequest(
-    const std::string& path,
-    const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
-    const content::URLDataSource::GotDataCallback& callback) {
-  if (!large_icon_service_) {
-    SendNotFoundResponse(callback);
-    return;
-  }
-
-  LargeIconUrlParser parser;
-  bool success = parser.Parse(path);
-  if (!success ||
-      parser.size_in_pixels() <= 0 ||
-      parser.size_in_pixels() > kMaxLargeIconSize) {
-    SendNotFoundResponse(callback);
-    return;
-  }
-
-  GURL url(parser.url_string());
-  if (!url.is_valid()) {
-    SendNotFoundResponse(callback);
-    return;
-  }
-
-  // TODO(beaudoin): Potentially allow icon to be scaled up.
-  large_icon_service_->GetLargeIconOrFallbackStyle(
-      url,
-      parser.size_in_pixels(),  // Reducing this will enable scale up.
-      parser.size_in_pixels(),
-      base::Bind(&LargeIconSource::OnLargeIconDataAvailable,
-                 base::Unretained(this), callback, url,
-                 parser.size_in_pixels()),
-      &cancelable_task_tracker_);
-}
-
-std::string LargeIconSource::GetMimeType(const std::string&) const {
-  // We need to explicitly return a mime type, otherwise if the user tries to
-  // drag the image they get no extension.
-  return "image/png";
-}
-
-bool LargeIconSource::AllowCaching() const {
-  return false;
-}
-
-bool LargeIconSource::ShouldReplaceExistingSource() const {
-  // Leave the existing DataSource in place, otherwise we'll drop any pending
-  // requests on the floor.
-  return false;
-}
-
-bool LargeIconSource::ShouldServiceRequest(
-    const GURL& url,
-    content::ResourceContext* resource_context,
-    int render_process_id) const {
-  if (url.SchemeIs(chrome::kChromeSearchScheme)) {
-    return InstantIOContext::ShouldServiceRequest(url, resource_context,
-                                                  render_process_id);
-  }
-  return URLDataSource::ShouldServiceRequest(url, resource_context,
-                                             render_process_id);
-}
-
-void LargeIconSource::OnLargeIconDataAvailable(
-    const content::URLDataSource::GotDataCallback& callback,
-    const GURL& url,
-    int size,
-    const favicon_base::LargeIconResult& result) {
-  if (result.bitmap.is_valid()) {
-    callback.Run(result.bitmap.bitmap_data.get());
-    return;
-  }
-
-  if (!result.fallback_icon_style) {
-    SendNotFoundResponse(callback);
-    return;
-  }
-
-  // RenderFallbackIconBitmap() cannot draw fallback icons on Android. See
-  // crbug.com/580922 for details. Return a 1x1 bitmap so that JavaScript can
-  // detect that it needs to generate a fallback icon.
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(1, 1);
-  bitmap.eraseColor(result.fallback_icon_style->background_color);
-  std::vector<unsigned char> bitmap_data;
-  if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data))
-    bitmap_data.clear();
-
-  callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
-}
-
-void LargeIconSource::SendNotFoundResponse(
-    const content::URLDataSource::GotDataCallback& callback) {
-  callback.Run(nullptr);
-}
diff --git a/chrome/browser/ui/webui/large_icon_source.h b/chrome/browser/ui/webui/large_icon_source.h
deleted file mode 100644
index 6d48d908..0000000
--- a/chrome/browser/ui/webui/large_icon_source.h
+++ /dev/null
@@ -1,76 +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 CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
-#define CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "content/public/browser/url_data_source.h"
-
-namespace favicon {
-class LargeIconService;
-}
-
-namespace favicon_base {
-struct LargeIconResult;
-}
-
-// LargeIconSource services explicit chrome:// requests for large icons.
-//
-// Format:
-//   chrome://large-icon/size/url
-//
-// Parameter:
-//  'size'             Required (including trailing '/')
-//    Positive integer to specify the large icon's size in pixels.
-//  'url'              Optional
-//    String to specify the page URL of the large icon.
-//
-//  Example: chrome://large-icon/48/http://www.google.com/
-//    This requests a 48x48 large icon for http://www.google.com.
-class LargeIconSource : public content::URLDataSource {
- public:
-  // |large_icon_service| is owned by caller and may be null.
-  explicit LargeIconSource(favicon::LargeIconService* large_icon_service);
-
-  ~LargeIconSource() override;
-
-  // content::URLDataSource implementation.
-  std::string GetSource() const override;
-  void StartDataRequest(
-      const std::string& path,
-      const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
-      const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
-  bool AllowCaching() const override;
-  bool ShouldReplaceExistingSource() const override;
-  bool ShouldServiceRequest(const GURL& url,
-                            content::ResourceContext* resource_context,
-                            int render_process_id) const override;
-
- private:
-  // Called with results of large icon retrieval request.
-  void OnLargeIconDataAvailable(
-      const content::URLDataSource::GotDataCallback& callback,
-      const GURL& url,
-      int size,
-      const favicon_base::LargeIconResult& bitmap_result);
-
-  // Returns null to trigger "Not Found" response.
-  void SendNotFoundResponse(
-      const content::URLDataSource::GotDataCallback& callback);
-
-  base::CancelableTaskTracker cancelable_task_tracker_;
-
-  // Owned by client.
-  favicon::LargeIconService* large_icon_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(LargeIconSource);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
diff --git a/chrome/browser/ui/webui/media_router/media_cast_mode.cc b/chrome/browser/ui/webui/media_router/media_cast_mode.cc
index 49af3be..7a5a75a9 100644
--- a/chrome/browser/ui/webui/media_router/media_cast_mode.cc
+++ b/chrome/browser/ui/webui/media_router/media_cast_mode.cc
@@ -11,18 +11,19 @@
 
 namespace media_router {
 
-std::string MediaCastModeToDescription(
-    MediaCastMode mode, const std::string& host) {
+std::string MediaCastModeToDescription(MediaCastMode mode,
+                                       const std::string& host) {
   switch (mode) {
     case MediaCastMode::PRESENTATION:
       return l10n_util::GetStringFUTF8(IDS_MEDIA_ROUTER_PRESENTATION_CAST_MODE,
                                        base::UTF8ToUTF16(host));
     case MediaCastMode::TAB_MIRROR:
-      return l10n_util::GetStringUTF8(
-          IDS_MEDIA_ROUTER_TAB_MIRROR_CAST_MODE);
+      return l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_TAB_MIRROR_CAST_MODE);
     case MediaCastMode::DESKTOP_MIRROR:
       return l10n_util::GetStringUTF8(
           IDS_MEDIA_ROUTER_DESKTOP_MIRROR_CAST_MODE);
+    case MediaCastMode::LOCAL_FILE:
+      return l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_LOCAL_FILE_CAST_MODE);
     default:
       NOTREACHED();
       return "";
@@ -34,6 +35,7 @@
     case MediaCastMode::PRESENTATION:
     case MediaCastMode::TAB_MIRROR:
     case MediaCastMode::DESKTOP_MIRROR:
+    case MediaCastMode::LOCAL_FILE:
       return true;
     default:
       return false;
diff --git a/chrome/browser/ui/webui/media_router/media_cast_mode.h b/chrome/browser/ui/webui/media_router/media_cast_mode.h
index d5566547..3916ab9 100644
--- a/chrome/browser/ui/webui/media_router/media_cast_mode.h
+++ b/chrome/browser/ui/webui/media_router/media_cast_mode.h
@@ -26,6 +26,8 @@
   // Capture the entire desktop and stream it to a media sink.  Available when
   // there is a compatible sink.
   DESKTOP_MIRROR = 0x4,
+  // Take a local media file to open in a tab and cast.
+  LOCAL_FILE = 0x8,
 };
 
 using CastModeSet = std::set<MediaCastMode>;
diff --git a/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc b/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
new file mode 100644
index 0000000..2d5e9ee0
--- /dev/null
+++ b/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/media_router/media_router_file_dialog.h"
+
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "net/base/filename_util.h"
+
+namespace media_router {
+
+MediaRouterFileDialog::MediaRouterFileDialog(
+    MediaRouterFileDialogDelegate* delegate)
+    : delegate_(delegate) {}
+
+MediaRouterFileDialog::~MediaRouterFileDialog() = default;
+
+GURL MediaRouterFileDialog::GetLastSelectedFileUrl() {
+  if (!selected_file_.has_value())
+    return GURL();
+
+  return net::FilePathToFileURL(selected_file_->local_path);
+}
+
+base::string16 MediaRouterFileDialog::GetLastSelectedFileName() {
+  if (!selected_file_.has_value())
+    return base::string16();
+
+  return selected_file_.value().file_path.BaseName().LossyDisplayName();
+}
+
+base::string16 MediaRouterFileDialog::GetDetailedErrorMessage() {
+  return detailed_error_message_.value_or(base::string16());
+}
+
+void MediaRouterFileDialog::OpenFileDialog(Browser* browser) {
+  select_file_dialog_ = ui::SelectFileDialog::Create(
+      this, new ChromeSelectFilePolicy(
+                browser->tab_strip_model()->GetActiveWebContents()));
+
+  const base::FilePath directory =
+      browser->profile()->last_selected_directory();
+
+  gfx::NativeWindow parent_window = browser->window()->GetNativeWindow();
+
+  ui::SelectFileDialog::FileTypeInfo file_types;
+  file_types.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
+
+  select_file_dialog_->SelectFile(
+      ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(), directory,
+      &file_types, 0, base::FilePath::StringType(), parent_window, nullptr);
+}
+
+void MediaRouterFileDialog::FileSelected(const base::FilePath& path,
+                                         int index,
+                                         void* params) {
+  FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
+}
+
+void MediaRouterFileDialog::FileSelectedWithExtraInfo(
+    const ui::SelectedFileInfo& file_info,
+    int index,
+    void* params) {
+  // TODO(offenwanger): Validate file.
+  selected_file_ = file_info;
+  delegate_->FileDialogFileSelected(file_info);
+}
+
+void MediaRouterFileDialog::FileSelectionCanceled(void* params) {
+  delegate_->FileDialogSelectionFailed(CANCELED);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/ui/webui/media_router/media_router_file_dialog.h b/chrome/browser/ui/webui/media_router/media_router_file_dialog.h
new file mode 100644
index 0000000..0115b19
--- /dev/null
+++ b/chrome/browser/ui/webui/media_router/media_router_file_dialog.h
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_FILE_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_FILE_DIALOG_H_
+
+#include "base/files/file_path.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+#include "url/gurl.h"
+
+namespace media_router {
+
+class MediaRouterFileDialog : public ui::SelectFileDialog::Listener {
+ public:
+  // The reasons that the file selection might have failed. Passed into the
+  // failure callback.
+  enum FailureReason {
+    // User canceled dialog, probably most common reason.
+    CANCELED,
+    // The file is invalid, it does not pass the validity checks.
+    INVALID_FILE,
+    // The reason for the failure is unknown.
+    UNKNOWN,
+  };
+
+  class MediaRouterFileDialogDelegate {
+   public:
+    virtual ~MediaRouterFileDialogDelegate() {}
+
+    // Called when a file is selected by the user to store the files information
+    // and tell the message handler to pass along the information.
+    virtual void FileDialogFileSelected(
+        const ui::SelectedFileInfo& file_info) = 0;
+
+    // Called when the file selection fails for whatever reason, including user
+    // cancelation.
+    virtual void FileDialogSelectionFailed(FailureReason) = 0;
+  };
+
+  explicit MediaRouterFileDialog(MediaRouterFileDialogDelegate* delegate);
+  ~MediaRouterFileDialog() override;
+
+  GURL GetLastSelectedFileUrl();
+  base::string16 GetLastSelectedFileName();
+  base::string16 GetDetailedErrorMessage();
+
+  // Opens the dialog configured to get a media file.
+  void OpenFileDialog(Browser* browser);
+
+  // Overridden from SelectFileDialog::Listener:
+  void FileSelected(const base::FilePath& path,
+                    int index,
+                    void* params) override;
+  void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file_info,
+                                 int index,
+                                 void* params) override;
+  void FileSelectionCanceled(void* params) override;
+
+ private:
+  MediaRouterFileDialogDelegate* delegate_;
+
+  // Pointer to the file last indicated by the system.
+  base::Optional<ui::SelectedFileInfo> selected_file_;
+
+  // A string that stores additional error information
+  // TODO(offenwanger): Set this when a file is invalid.
+  base::Optional<base::string16> detailed_error_message_;
+
+  // The dialog object for the file dialog.
+  scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_FILE_DIALOG_H_
diff --git a/chrome/browser/ui/webui/media_router/media_router_file_dialog_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_file_dialog_unittest.cc
new file mode 100644
index 0000000..1adfb43
--- /dev/null
+++ b/chrome/browser/ui/webui/media_router/media_router_file_dialog_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/media_router/media_router_file_dialog.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::SaveArg;
+using testing::Test;
+
+namespace media_router {
+
+class MockDelegate
+    : public MediaRouterFileDialog::MediaRouterFileDialogDelegate {
+ public:
+  MOCK_METHOD1(FileDialogFileSelected,
+               void(const ui::SelectedFileInfo& file_info));
+  MOCK_METHOD1(FileDialogSelectionFailed,
+               void(const MediaRouterFileDialog::FailureReason reason));
+};
+
+class MediaRouterFileDialogTest : public Test {
+ public:
+  MediaRouterFileDialogTest() {
+    scoped_feature_list_.InitFromCommandLine(
+        "EnableCastLocalMedia" /* enabled features */,
+        std::string() /* disabled features */);
+  }
+
+  void SetUp() override {
+    dialog_ = base::MakeUnique<MediaRouterFileDialog>(&mock_delegate_);
+  }
+
+ protected:
+  MockDelegate mock_delegate_;
+  std::unique_ptr<MediaRouterFileDialog> dialog_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(MediaRouterFileDialogTest, SelectFileSuccess) {
+  // File selection succeeds, success callback called with the right file info.
+  // Selected file URL is set properly.
+  const std::string path_string = "im/a/path";
+  const base::FilePath path = base::FilePath(FILE_PATH_LITERAL("im/a/path"));
+  ui::SelectedFileInfo file_info;
+  EXPECT_CALL(mock_delegate_, FileDialogFileSelected(_))
+      .WillOnce(SaveArg<0>(&file_info));
+
+  dialog_->FileSelected(path, 0, 0);
+
+  ASSERT_EQ(file_info.local_path, path);
+  ASSERT_TRUE(dialog_->GetLastSelectedFileUrl().GetContent().find(
+                  path_string) != std::string::npos);
+}
+
+TEST_F(MediaRouterFileDialogTest, SelectFileCanceled) {
+  // File selection gets cancelled, failure callback called
+  EXPECT_CALL(mock_delegate_, FileDialogSelectionFailed(dialog_->CANCELED));
+
+  dialog_->FileSelectionCanceled(0);
+}
+
+// TODO(offenwanger): Add tests for invalid files when that code is added.
+
+}  // namespace media_router
diff --git a/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc b/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
index b28aadd..17af03a 100644
--- a/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
@@ -15,25 +15,26 @@
 
 void AddMediaRouterStrings(content::WebUIDataSource* html_source) {
   html_source->AddLocalizedString("mediaRouterTitle", IDS_MEDIA_ROUTER_TITLE);
-  html_source->AddLocalizedString("learnMoreText",
-                                  IDS_MEDIA_ROUTER_LEARN_MORE);
+  html_source->AddLocalizedString("learnMoreText", IDS_MEDIA_ROUTER_LEARN_MORE);
   html_source->AddLocalizedString("backButtonTitle",
                                   IDS_MEDIA_ROUTER_BACK_BUTTON_TITLE);
   html_source->AddLocalizedString("closeButtonTitle",
                                   IDS_MEDIA_ROUTER_CLOSE_BUTTON_TITLE);
   html_source->AddLocalizedString("searchButtonTitle",
                                   IDS_MEDIA_ROUTER_SEARCH_BUTTON_TITLE);
-  html_source->AddLocalizedString("viewCastModeListButtonTitle",
+  html_source->AddLocalizedString(
+      "viewCastModeListButtonTitle",
       IDS_MEDIA_ROUTER_VIEW_CAST_MODE_LIST_BUTTON_TITLE);
-  html_source->AddLocalizedString("viewDeviceListButtonTitle",
+  html_source->AddLocalizedString(
+      "viewDeviceListButtonTitle",
       IDS_MEDIA_ROUTER_VIEW_DEVICE_LIST_BUTTON_TITLE);
 }
 
 void AddRouteDetailsStrings(content::WebUIDataSource* html_source) {
   html_source->AddLocalizedString("castingActivityStatus",
-      IDS_MEDIA_ROUTER_CASTING_ACTIVITY_STATUS);
+                                  IDS_MEDIA_ROUTER_CASTING_ACTIVITY_STATUS);
   html_source->AddLocalizedString("stopCastingButtonText",
-      IDS_MEDIA_ROUTER_STOP_CASTING_BUTTON);
+                                  IDS_MEDIA_ROUTER_STOP_CASTING_BUTTON);
   html_source->AddLocalizedString("startCastingButtonText",
                                   IDS_MEDIA_ROUTER_START_CASTING_BUTTON);
   html_source->AddLocalizedString("playTitle",
@@ -58,13 +59,18 @@
 }
 
 void AddMediaRouterContainerStrings(content::WebUIDataSource* html_source) {
+  html_source->AddLocalizedString("castLocalMediaSubheadingText",
+                                  IDS_MEDIA_ROUTER_CAST_LOCAL_MEDIA_SUBHEADING);
+  html_source->AddLocalizedString("castLocalMediaSelectedFileTitle",
+                                  IDS_MEDIA_ROUTER_CAST_LOCAL_MEDIA_TITLE);
   html_source->AddLocalizedString("firstRunFlowButtonText",
                                   IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_BUTTON);
   html_source->AddLocalizedString("firstRunFlowText",
                                   IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_TEXT);
   html_source->AddLocalizedString("firstRunFlowTitle",
                                   IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_TITLE);
-  html_source->AddLocalizedString("firstRunFlowCloudPrefText",
+  html_source->AddLocalizedString(
+      "firstRunFlowCloudPrefText",
       IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_CLOUD_PREF_TEXT);
   html_source->AddLocalizedString("autoCastMode",
                                   IDS_MEDIA_ROUTER_AUTO_CAST_MODE);
@@ -75,8 +81,9 @@
   html_source->AddLocalizedString("searchNoMatchesText",
                                   IDS_MEDIA_ROUTER_SEARCH_NO_MATCHES);
   html_source->AddLocalizedString("selectCastModeHeaderText",
-      IDS_MEDIA_ROUTER_SELECT_CAST_MODE_HEADER);
-  html_source->AddLocalizedString("shareYourScreenSubheadingText",
+                                  IDS_MEDIA_ROUTER_SELECT_CAST_MODE_HEADER);
+  html_source->AddLocalizedString(
+      "shareYourScreenSubheadingText",
       IDS_MEDIA_ROUTER_SHARE_YOUR_SCREEN_SUBHEADING);
 }
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index dbc148b..6e82cef 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/media/router/issues_observer.h"
 #include "chrome/browser/media/router/media_router.h"
 #include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/media_router_metrics.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
 #include "chrome/browser/media/router/media_sinks_observer.h"
@@ -28,6 +29,10 @@
 #include "chrome/browser/media/router/presentation_service_delegate_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/media_router/media_router_resources_provider.h"
 #include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
@@ -61,10 +66,12 @@
 // The amount of time to wait for a response when creating a new route.
 const int kCreateRouteTimeoutSeconds = 20;
 const int kCreateRouteTimeoutSecondsForTab = 60;
+const int kCreateRouteTimeoutSecondsForLocalFile = 60;
 const int kCreateRouteTimeoutSecondsForDesktop = 120;
 
 std::string GetHostFromURL(const GURL& gurl) {
-  if (gurl.is_empty()) return std::string();
+  if (gurl.is_empty())
+    return std::string();
   std::string host = gurl.host();
   if (base::StartsWith(host, "www.", base::CompareCase::INSENSITIVE_ASCII))
     host = host.substr(4);
@@ -88,6 +95,9 @@
       return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForTab);
     case DESKTOP_MIRROR:
       return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForDesktop);
+    case LOCAL_FILE:
+      return base::TimeDelta::FromSeconds(
+          kCreateRouteTimeoutSecondsForLocalFile);
     default:
       NOTREACHED();
       return base::TimeDelta();
@@ -107,8 +117,10 @@
 
 // static
 std::string MediaRouterUI::GetExtensionName(
-    const GURL& gurl, extensions::ExtensionRegistry* registry) {
-  if (gurl.is_empty() || !registry) return std::string();
+    const GURL& gurl,
+    extensions::ExtensionRegistry* registry) {
+  if (gurl.is_empty() || !registry)
+    return std::string();
 
   const extensions::Extension* extension =
       registry->enabled_extensions().GetExtensionOrAppByURL(gurl);
@@ -116,6 +128,37 @@
   return extension ? extension->name() : std::string();
 }
 
+Browser* MediaRouterUI::GetBrowser() {
+  return chrome::FindBrowserWithWebContents(initiator());
+}
+
+void MediaRouterUI::OpenTabWithUrl(const GURL url) {
+  // Check if the current page is a new tab. If so open file in current page.
+  // If not then open a new page.
+  if (initiator_->GetVisibleURL() == chrome::kChromeUINewTabURL) {
+    content::NavigationController::LoadURLParams load_params(url);
+    load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
+    initiator_->GetController().LoadURLWithParams(load_params);
+  } else {
+    initiator_ = chrome::AddSelectedTabWithURL(GetBrowser(), url,
+                                               ui::PAGE_TRANSITION_LINK);
+  }
+}
+
+void MediaRouterUI::FileDialogFileSelected(
+    const ui::SelectedFileInfo& file_info) {
+  handler_->UserSelectedLocalMediaFile(file_info.display_name);
+}
+
+void MediaRouterUI::FileDialogSelectionFailed(
+    MediaRouterFileDialog::FailureReason reason) {
+  if (reason == MediaRouterFileDialog::CANCELED) {
+    VLOG(2) << "File selection cancelled.";
+  } else {
+    VLOG(0) << "File selection errored!";
+  }
+}
+
 // This class calls to refresh the UI when the highest priority issue is
 // updated.
 class MediaRouterUI::UIIssuesObserver : public IssuesObserver {
@@ -211,7 +254,8 @@
 }
 
 MediaRouterUI::~MediaRouterUI() {
-  if (query_result_manager_.get()) query_result_manager_->RemoveObserver(this);
+  if (query_result_manager_.get())
+    query_result_manager_->RemoveObserver(this);
   if (presentation_service_delegate_.get())
     presentation_service_delegate_->RemoveDefaultPresentationRequestObserver(
         this);
@@ -273,8 +317,7 @@
   presentation_service_delegate_ = delegate->GetWeakPtr();
 
   InitCommon(initiator);
-  OnDefaultPresentationChanged(
-      create_session_request_->presentation_request());
+  OnDefaultPresentationChanged(create_session_request_->presentation_request());
 }
 
 void MediaRouterUI::InitCommon(content::WebContents* initiator) {
@@ -311,6 +354,13 @@
   // Desktop mirror mode is always available.
   query_result_manager_->SetSourcesForCastMode(
       MediaCastMode::DESKTOP_MIRROR, {MediaSourceForDesktop()}, origin);
+
+  // For now, file mirroring is always availible if enabled.
+  if (CastLocalMediaEnabled()) {
+    query_result_manager_->SetSourcesForCastMode(
+        MediaCastMode::LOCAL_FILE, {MediaSourceForTab(0)}, origin);
+  }
+
   initiator_ = initiator;
   SessionID::id_type tab_id = SessionTabHelper::IdForTab(initiator);
   if (tab_id != -1) {
@@ -330,11 +380,12 @@
     MediaRouter* router,
     content::WebContents* initiator,
     MediaRouterWebUIMessageHandler* handler,
-    std::unique_ptr<CreatePresentationConnectionRequest>
-        create_session_request) {
+    std::unique_ptr<CreatePresentationConnectionRequest> create_session_request,
+    std::unique_ptr<MediaRouterFileDialog> file_dialog) {
   router_ = router;
   handler_ = handler;
   create_session_request_ = std::move(create_session_request);
+  media_router_file_dialog_ = std::move(file_dialog);
   InitCommon(initiator);
   if (create_session_request_) {
     OnDefaultPresentationChanged(
@@ -431,11 +482,18 @@
   std::vector<MediaRouteResponseCallback> route_response_callbacks;
   base::TimeDelta timeout;
   bool incognito;
+
+  if (cast_mode == MediaCastMode::LOCAL_FILE) {
+    GURL url = media_router_file_dialog_->GetLastSelectedFileUrl();
+    OpenTabWithUrl(url);
+  }
+
   if (!SetRouteParameters(sink_id, cast_mode, &source_id, &origin,
                           &route_response_callbacks, &timeout, &incognito)) {
     SendIssueForUnableToCast(cast_mode);
     return false;
   }
+
   router_->CreateRoute(source_id, sink_id, origin, initiator_,
                        route_response_callbacks, timeout, incognito);
   return true;
@@ -458,8 +516,10 @@
   // called. However, since the user does not have visibility into the
   // MediaSource, and that it occurs very rarely in practice, we leave it as-is
   // for now.
+
   std::unique_ptr<MediaSource> source =
       query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
+
   if (!source) {
     LOG(ERROR) << "No corresponding MediaSource for cast mode "
                << static_cast<int>(cast_mode) << " and sink " << sink_id;
@@ -550,6 +610,14 @@
   router_->ClearIssue(issue_id);
 }
 
+void MediaRouterUI::OpenFileDialog() {
+  if (!media_router_file_dialog_) {
+    media_router_file_dialog_ = base::MakeUnique<MediaRouterFileDialog>(this);
+  }
+
+  media_router_file_dialog_->OpenFileDialog(GetBrowser());
+}
+
 void MediaRouterUI::SearchSinksAndCreateRoute(
     const MediaSink::Id& sink_id,
     const std::string& search_criteria,
@@ -561,10 +629,9 @@
 
   // The CreateRoute() part of the function is accomplished in the callback
   // OnSearchSinkResponseReceived().
-  router_->SearchSinks(
-      sink_id, source_id, search_criteria, domain,
-      base::Bind(&MediaRouterUI::OnSearchSinkResponseReceived,
-                 weak_factory_.GetWeakPtr(), cast_mode));
+  router_->SearchSinks(sink_id, source_id, search_criteria, domain,
+                       base::Bind(&MediaRouterUI::OnSearchSinkResponseReceived,
+                                  weak_factory_.GetWeakPtr(), cast_mode));
 }
 
 bool MediaRouterUI::UserSelectedTabMirroringForCurrentOrigin() const {
@@ -591,6 +658,9 @@
       // Desktop mirroring isn't domain-specific, so we don't record the
       // selection.
       break;
+    case MediaCastMode::LOCAL_FILE:
+      // Local media isn't domain-specific, so we don't record the selection.
+      break;
     default:
       NOTREACHED();
       break;
@@ -662,7 +732,8 @@
     const RouteRequestResult& result) {
   DVLOG(1) << "OnRouteResponseReceived";
   // If we receive a new route that we aren't expecting, do nothing.
-  if (route_request_id != current_route_request_id_) return;
+  if (route_request_id != current_route_request_id_)
+    return;
 
   const MediaRoute* route = result.route();
   if (!route) {
@@ -781,15 +852,15 @@
 
 void MediaRouterUI::OnUIInitiallyLoaded() {
   if (!start_time_.is_null()) {
-    MediaRouterMetrics::RecordMediaRouterDialogPaint(
-        base::Time::Now() - start_time_);
+    MediaRouterMetrics::RecordMediaRouterDialogPaint(base::Time::Now() -
+                                                     start_time_);
   }
 }
 
 void MediaRouterUI::OnUIInitialDataReceived() {
   if (!start_time_.is_null()) {
-    MediaRouterMetrics::RecordMediaRouterDialogLoaded(
-        base::Time::Now() - start_time_);
+    MediaRouterMetrics::RecordMediaRouterDialogLoaded(base::Time::Now() -
+                                                      start_time_);
     start_time_ = base::Time();
   }
 }
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.h b/chrome/browser/ui/webui/media_router/media_router_ui.h
index 18c490f..388600956 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -18,6 +18,7 @@
 #include "chrome/browser/media/router/presentation_service_delegate_impl.h"
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/webui/media_router/media_router_file_dialog.h"
 #include "chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h"
 #include "chrome/browser/ui/webui/media_router/query_result_manager.h"
 #include "chrome/common/media_router/issue.h"
@@ -38,6 +39,8 @@
 class Collator;
 }
 
+class Browser;
+
 namespace media_router {
 
 class CreatePresentationConnectionRequest;
@@ -50,10 +53,12 @@
 class RouteRequestResult;
 
 // Implements the chrome://media-router user interface.
-class MediaRouterUI : public ConstrainedWebDialogUI,
-                      public QueryResultManager::Observer,
-                      public PresentationServiceDelegateImpl::
-                          DefaultPresentationRequestObserver {
+class MediaRouterUI
+    : public ConstrainedWebDialogUI,
+      public QueryResultManager::Observer,
+      public PresentationServiceDelegateImpl::
+          DefaultPresentationRequestObserver,
+      public MediaRouterFileDialog::MediaRouterFileDialogDelegate {
  public:
   // |web_ui| owns this object and is used to initialize the base class.
   explicit MediaRouterUI(content::WebUI* web_ui);
@@ -119,6 +124,9 @@
   // Calls MediaRouter to clear the given issue.
   void ClearIssue(const Issue::Id& issue_id);
 
+  // Called to open a file dialog with the media_router_ui file dialog handler.
+  void OpenFileDialog();
+
   // Calls MediaRouter to search route providers for sinks matching
   // |search_criteria| with the source that is currently associated with
   // |cast_mode|. The user's domain |domain| is also used.
@@ -181,7 +189,8 @@
                    content::WebContents* initiator,
                    MediaRouterWebUIMessageHandler* handler,
                    std::unique_ptr<CreatePresentationConnectionRequest>
-                       create_session_request);
+                       create_session_request,
+                   std::unique_ptr<MediaRouterFileDialog> file_dialog);
 
  private:
   friend class MediaRouterUITest;
@@ -191,9 +200,9 @@
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, FilterNonDisplayRoutes);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, FilterNonDisplayJoinableRoutes);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
-      UIMediaRoutesObserverAssignsCurrentCastModes);
+                           UIMediaRoutesObserverAssignsCurrentCastModes);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
-      UIMediaRoutesObserverSkipsUnavailableCastModes);
+                           UIMediaRoutesObserverSkipsUnavailableCastModes);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, GetExtensionNameExtensionPresent);
   FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
                            GetExtensionNameEmptyWhenNotInstalled);
@@ -251,6 +260,17 @@
   static std::string GetExtensionName(const GURL& url,
                                       extensions::ExtensionRegistry* registry);
 
+  // Retrieves the browser associated with this UI.
+  Browser* GetBrowser();
+
+  // Opens the URL in a tab which is then |initator_|.
+  void OpenTabWithUrl(const GURL url);
+
+  // Methods for MediaRouterFileDialogDelegate
+  void FileDialogFileSelected(const ui::SelectedFileInfo& file_info) override;
+  void FileDialogSelectionFailed(
+      MediaRouterFileDialog::FailureReason reason) override;
+
   // QueryResultManager::Observer
   void OnResultsUpdated(
       const std::vector<MediaSinkWithCastModes>& sinks) override;
@@ -396,6 +416,10 @@
   // updates.
   std::unique_ptr<UIMediaRouteControllerObserver> route_controller_observer_;
 
+  // The dialog that handles opening the file dialog and validating and
+  // returning the results.
+  std::unique_ptr<MediaRouterFileDialog> media_router_file_dialog_;
+
   // If set, a cast mode that is required to be shown first.
   base::Optional<MediaCastMode> forced_cast_mode_;
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index f7ca9d9..c63f053 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/media/router/create_presentation_connection_request.h"
 #include "chrome/browser/media/router/mock_media_router.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_test.h"
@@ -18,6 +19,7 @@
 #include "chrome/common/media_router/media_route.h"
 #include "chrome/common/media_router/media_source_helper.h"
 #include "chrome/common/media_router/route_request_result.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -79,6 +81,11 @@
   MediaRouterUITest() {
     ON_CALL(mock_router_, GetCurrentRoutes())
         .WillByDefault(Return(std::vector<MediaRoute>()));
+
+    // enable and disable features
+    scoped_feature_list_.InitFromCommandLine(
+        "EnableCastLocalMedia" /* enabled features */,
+        std::string() /* disabled features */);
   }
 
   void TearDown() override {
@@ -109,6 +116,12 @@
     media_router_ui_ = base::MakeUnique<MediaRouterUI>(&web_ui_);
     message_handler_ = base::MakeUnique<MockMediaRouterWebUIMessageHandler>(
         media_router_ui_.get());
+
+    auto file_dialog =
+        base::MakeUnique<MediaRouterFileDialog>(media_router_ui_.get());
+
+    mock_file_dialog_ = file_dialog.get();
+
     EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
         .WillRepeatedly(Invoke([this](MediaSinksObserver* observer) {
           this->media_sinks_observers_.push_back(observer);
@@ -116,9 +129,9 @@
         }));
     EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_))
         .Times(AnyNumber());
-    media_router_ui_->InitForTest(&mock_router_, web_contents(),
-                                  message_handler_.get(),
-                                  std::move(create_session_request_));
+    media_router_ui_->InitForTest(
+        &mock_router_, web_contents(), message_handler_.get(),
+        std::move(create_session_request_), std::move(file_dialog));
     message_handler_->SetWebUIForTest(&web_ui_);
   }
 
@@ -161,7 +174,9 @@
   std::unique_ptr<CreatePresentationConnectionRequest> create_session_request_;
   std::unique_ptr<MediaRouterUI> media_router_ui_;
   std::unique_ptr<MockMediaRouterWebUIMessageHandler> message_handler_;
+  MediaRouterFileDialog* mock_file_dialog_ = nullptr;
   std::vector<MediaSinksObserver*> media_sinks_observers_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(MediaRouterUITest, RouteCreationTimeoutForTab) {
@@ -226,6 +241,33 @@
     callback.Run(*result);
 }
 
+// Tests that if a local file CreateRoute call is made from a new tab, the
+// file will be opened in the new tab.
+TEST_F(MediaRouterUITest, RouteCreationLocalFileModeInTab) {
+  const GURL empty_tab = GURL(chrome::kChromeUINewTabURL);
+  const std::string file_path = "some/url/for/a/file.mp3";
+
+  // Setup the UI
+  CreateMediaRouterUIForURL(profile(), empty_tab);
+
+  mock_file_dialog_->FileSelected(
+      base::FilePath(FILE_PATH_LITERAL("some/url/for/a/file.mp3")), 0, 0);
+
+  content::WebContents* location_file_opened = nullptr;
+
+  // Expect that the media_router_ will make a call to the mock_router
+  // then we will want to check that it made the call with.
+  EXPECT_CALL(mock_router_, CreateRoute(_, _, _, _, _, _, _))
+      .WillOnce(SaveArg<3>(&location_file_opened));
+
+  media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
+                                MediaCastMode::LOCAL_FILE);
+
+  ASSERT_EQ(location_file_opened, web_contents());
+  ASSERT_TRUE(location_file_opened->GetVisibleURL().GetContent().find(
+                  file_path) != std::string::npos);
+}
+
 TEST_F(MediaRouterUITest, RouteCreationParametersCantBeCreated) {
   CreateMediaRouterUI(profile());
   MediaSinkSearchResponseCallback sink_callback;
@@ -723,8 +765,9 @@
   // initializing the dialog with a presentation request.  The WebUI can handle
   // the forced mode that is not in the initial cast mode set, but is this a
   // bug?
-  CastModeSet expected_modes(
-      {MediaCastMode::TAB_MIRROR, MediaCastMode::DESKTOP_MIRROR});
+  CastModeSet expected_modes({MediaCastMode::TAB_MIRROR,
+                              MediaCastMode::DESKTOP_MIRROR,
+                              MediaCastMode::LOCAL_FILE});
   EXPECT_CALL(*message_handler_,
               UpdateCastModes(
                   expected_modes, "",
@@ -737,7 +780,7 @@
   media_router_ui_->UIInitialized();
   media_router_ui_->InitForTest(&mock_router_, web_contents(),
                                 message_handler_.get(),
-                                std::move(create_session_request_));
+                                std::move(create_session_request_), nullptr);
   // |media_router_ui_| takes ownership of |request_callbacks|.
   media_router_ui_.reset();
 }
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
index dda4615..7346b30 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
@@ -60,6 +60,8 @@
 const char kReportSinkCount[] = "reportSinkCount";
 const char kReportTimeToClickSink[] = "reportTimeToClickSink";
 const char kReportTimeToInitialActionClose[] = "reportTimeToInitialActionClose";
+const char kReportWebUIRouteControllerLoaded[] =
+    "reportWebUIRouteControllerLoaded";
 const char kSearchSinksAndCreateRoute[] = "searchSinksAndCreateRoute";
 const char kOnInitialDataReceived[] = "onInitialDataReceived";
 const char kOnMediaControllerAvailable[] = "onMediaControllerAvailable";
@@ -67,6 +69,7 @@
 const char kPauseCurrentMedia[] = "pauseCurrentMedia";
 const char kPlayCurrentMedia[] = "playCurrentMedia";
 const char kSeekCurrentMedia[] = "seekCurrentMedia";
+const char kSelectLocalMediaFile[] = "selectLocalMediaFile";
 const char kSetCurrentMediaMute[] = "setCurrentMediaMute";
 const char kSetCurrentMediaVolume[] = "setCurrentMediaVolume";
 
@@ -84,6 +87,8 @@
 const char kSetCastModeList[] = "media_router.ui.setCastModeList";
 const char kUpdateMaxHeight[] = "media_router.ui.updateMaxHeight";
 const char kUpdateRouteStatus[] = "media_router.ui.updateRouteStatus";
+const char kUserSelectedLocalMediaFile[] =
+    "media_router.ui.userSelectedLocalMediaFile";
 const char kWindowOpen[] = "window.open";
 
 std::unique_ptr<base::DictionaryValue> SinksAndIdentityToValue(
@@ -165,12 +170,11 @@
 
   const std::string& custom_path = route.custom_controller_path();
   if (!incognito && !custom_path.empty()) {
-    std::string full_custom_controller_path = base::StringPrintf("%s://%s/%s",
-        extensions::kExtensionScheme, extension_id.c_str(),
-            custom_path.c_str());
+    std::string full_custom_controller_path =
+        base::StringPrintf("%s://%s/%s", extensions::kExtensionScheme,
+                           extension_id.c_str(), custom_path.c_str());
     DCHECK(GURL(full_custom_controller_path).is_valid());
-    dictionary->SetString("customControllerPath",
-                          full_custom_controller_path);
+    dictionary->SetString("customControllerPath", full_custom_controller_path);
   }
 
   return dictionary;
@@ -254,8 +258,7 @@
       dialog_closing_(false),
       media_router_ui_(media_router_ui) {}
 
-MediaRouterWebUIMessageHandler::~MediaRouterWebUIMessageHandler() {
-}
+MediaRouterWebUIMessageHandler::~MediaRouterWebUIMessageHandler() {}
 
 void MediaRouterWebUIMessageHandler::UpdateSinks(
     const std::vector<MediaSinkWithCastModes>& sinks) {
@@ -352,47 +355,47 @@
   web_ui()->CallJavascriptFunctionUnsafe(kOnRouteControllerInvalidated);
 }
 
+void MediaRouterWebUIMessageHandler::UserSelectedLocalMediaFile(
+    base::FilePath::StringType file_name) {
+  DVLOG(2) << "UserSelectedLocalMediaFile";
+  web_ui()->CallJavascriptFunctionUnsafe(kUserSelectedLocalMediaFile,
+                                         base::Value(file_name));
+}
+
 void MediaRouterWebUIMessageHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
       kRequestInitialData,
       base::Bind(&MediaRouterWebUIMessageHandler::OnRequestInitialData,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kCreateRoute,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnCreateRoute,
-                 base::Unretained(this)));
+      kCreateRoute, base::Bind(&MediaRouterWebUIMessageHandler::OnCreateRoute,
+                               base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       kAcknowledgeFirstRunFlow,
       base::Bind(&MediaRouterWebUIMessageHandler::OnAcknowledgeFirstRunFlow,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kActOnIssue,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnActOnIssue,
-                 base::Unretained(this)));
+      kActOnIssue, base::Bind(&MediaRouterWebUIMessageHandler::OnActOnIssue,
+                              base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kCloseRoute,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnCloseRoute,
-                 base::Unretained(this)));
+      kCloseRoute, base::Bind(&MediaRouterWebUIMessageHandler::OnCloseRoute,
+                              base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kJoinRoute,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnJoinRoute,
-                 base::Unretained(this)));
+      kJoinRoute, base::Bind(&MediaRouterWebUIMessageHandler::OnJoinRoute,
+                             base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kCloseDialog,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnCloseDialog,
-                 base::Unretained(this)));
+      kCloseDialog, base::Bind(&MediaRouterWebUIMessageHandler::OnCloseDialog,
+                               base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kReportBlur,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnReportBlur,
-                 base::Unretained(this)));
+      kReportBlur, base::Bind(&MediaRouterWebUIMessageHandler::OnReportBlur,
+                              base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       kReportClickedSinkIndex,
       base::Bind(&MediaRouterWebUIMessageHandler::OnReportClickedSinkIndex,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      kReportFilter,
-      base::Bind(&MediaRouterWebUIMessageHandler::OnReportFilter,
-                 base::Unretained(this)));
+      kReportFilter, base::Bind(&MediaRouterWebUIMessageHandler::OnReportFilter,
+                                base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       kReportInitialState,
       base::Bind(&MediaRouterWebUIMessageHandler::OnReportInitialState,
@@ -431,6 +434,11 @@
           &MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose,
           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      kReportWebUIRouteControllerLoaded,
+      base::Bind(
+          &MediaRouterWebUIMessageHandler::OnReportWebUIRouteControllerLoaded,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       kSearchSinksAndCreateRoute,
       base::Bind(&MediaRouterWebUIMessageHandler::OnSearchSinksAndCreateRoute,
                  base::Unretained(this)));
@@ -459,6 +467,10 @@
       base::Bind(&MediaRouterWebUIMessageHandler::OnSeekCurrentMedia,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      kSelectLocalMediaFile,
+      base::Bind(&MediaRouterWebUIMessageHandler::OnSelectLocalMediaFile,
+                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       kSetCurrentMediaMute,
       base::Bind(&MediaRouterWebUIMessageHandler::OnSetCurrentMediaMute,
                  base::Unretained(this)));
@@ -476,7 +488,7 @@
 
   // "No Cast devices found?" Chromecast help center page.
   initial_data.SetString("deviceMissingUrl",
-      base::StringPrintf(kHelpPageUrlPrefix, 3249268));
+                         base::StringPrintf(kHelpPageUrlPrefix, 3249268));
 
   std::unique_ptr<base::DictionaryValue> sinks_and_identity(
       SinksAndIdentityToValue(media_router_ui_->sinks(), GetAccountInfo()));
@@ -576,8 +588,7 @@
   pref_service->SetBoolean(prefs::kMediaRouterCloudServicesPrefSet, true);
 }
 
-void MediaRouterWebUIMessageHandler::OnActOnIssue(
-    const base::ListValue* args) {
+void MediaRouterWebUIMessageHandler::OnActOnIssue(const base::ListValue* args) {
   DVLOG(1) << "OnActOnIssue";
   const base::DictionaryValue* args_dict = nullptr;
   Issue::Id issue_id;
@@ -667,16 +678,15 @@
   }
 
   if (used_esc_to_close_dialog) {
-    base::RecordAction(base::UserMetricsAction(
-        "MediaRouter_Ui_Dialog_ESCToClose"));
+    base::RecordAction(
+        base::UserMetricsAction("MediaRouter_Ui_Dialog_ESCToClose"));
   }
 
   dialog_closing_ = true;
   media_router_ui_->Close();
 }
 
-void MediaRouterWebUIMessageHandler::OnReportBlur(
-    const base::ListValue* args) {
+void MediaRouterWebUIMessageHandler::OnReportBlur(const base::ListValue* args) {
   DVLOG(1) << "OnReportBlur";
   base::RecordAction(base::UserMetricsAction("MediaRouter_Ui_Dialog_Blur"));
 }
@@ -699,7 +709,7 @@
 }
 
 void MediaRouterWebUIMessageHandler::OnReportInitialAction(
-  const base::ListValue* args) {
+    const base::ListValue* args) {
   DVLOG(1) << "OnReportInitialAction";
   int action;
   if (!args->GetInteger(0, &action)) {
@@ -724,7 +734,7 @@
 }
 
 void MediaRouterWebUIMessageHandler::OnReportNavigateToView(
-  const base::ListValue* args) {
+    const base::ListValue* args) {
   DVLOG(1) << "OnReportNavigateToView";
   std::string view;
   if (!args->GetString(0, &view)) {
@@ -733,8 +743,8 @@
   }
 
   if (view == "cast-mode-list") {
-    base::RecordAction(base::UserMetricsAction(
-        "MediaRouter_Ui_Navigate_SinkListToSource"));
+    base::RecordAction(
+        base::UserMetricsAction("MediaRouter_Ui_Navigate_SinkListToSource"));
   } else if (view == "route-details") {
     base::RecordAction(base::UserMetricsAction(
         "MediaRouter_Ui_Navigate_SinkListToRouteDetails"));
@@ -808,6 +818,18 @@
                       base::TimeDelta::FromMillisecondsD(time_to_click));
 }
 
+void MediaRouterWebUIMessageHandler::OnReportWebUIRouteControllerLoaded(
+    const base::ListValue* args) {
+  DVLOG(1) << "OnReportWebUIRouteControllerLoaded";
+  double load_time;
+  if (!args->GetDouble(0, &load_time)) {
+    DVLOG(1) << "Unable to extract args.";
+    return;
+  }
+  UMA_HISTOGRAM_TIMES("MediaRouter.Ui.Dialog.LoadedWebUiRouteController",
+                      base::TimeDelta::FromMillisecondsD(load_time));
+}
+
 void MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose(
     const base::ListValue* args) {
   DVLOG(1) << "OnReportTimeToInitialActionClose";
@@ -853,6 +875,11 @@
       static_cast<MediaCastMode>(cast_mode_num));
 }
 
+void MediaRouterWebUIMessageHandler::OnSelectLocalMediaFile(
+    const base::ListValue* args) {
+  media_router_ui_->OpenFileDialog();
+}
+
 void MediaRouterWebUIMessageHandler::OnInitialDataReceived(
     const base::ListValue* args) {
   DVLOG(1) << "OnInitialDataReceived";
@@ -983,15 +1010,15 @@
       if (first_run_flow_acknowledged &&
           ProfileSyncServiceFactory::GetForProfile(profile)->IsSyncActive()) {
         pref_service->SetBoolean(prefs::kMediaRouterEnableCloudServices, true);
-        pref_service->SetBoolean(prefs::kMediaRouterCloudServicesPrefSet,
-                                 true);
+        pref_service->SetBoolean(prefs::kMediaRouterCloudServicesPrefSet, true);
         // Return early since the first run flow won't be surfaced.
         return;
       }
 
       show_cloud_pref = true;
       // "Casting to a Hangout from Chrome" Chromecast help center page.
-      first_run_flow_data.SetString("firstRunFlowCloudPrefLearnMoreUrl",
+      first_run_flow_data.SetString(
+          "firstRunFlowCloudPrefLearnMoreUrl",
           base::StringPrintf(kHelpPageUrlPrefix, 6320939));
     }
   }
@@ -1040,8 +1067,8 @@
   for (const MediaRoute& route : routes) {
     bool can_join =
         base::ContainsValue(joinable_route_ids, route.media_route_id());
-    int current_cast_mode = CurrentCastModeForRouteId(route.media_route_id(),
-                                                      current_cast_modes);
+    int current_cast_mode =
+        CurrentCastModeForRouteId(route.media_route_id(), current_cast_modes);
     std::unique_ptr<base::DictionaryValue> route_val(RouteToValue(
         route, can_join, extension_id, incognito_, current_cast_mode));
     value->Append(std::move(route_val));
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
index ef4030b..1a881f1 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/optional.h"
 #include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
@@ -67,6 +68,10 @@
   // invalidated.
   void OnRouteControllerInvalidated();
 
+  // Called when the user has selected a file and the name should be relayed to
+  // the UI.
+  void UserSelectedLocalMediaFile(base::FilePath::StringType file_name);
+
   void SetWebUIForTest(content::WebUI* webui);
   void set_incognito_for_test(bool incognito) { incognito_ = incognito; }
 
@@ -105,10 +110,12 @@
   void OnReportSelectedCastMode(const base::ListValue* args);
   void OnReportSinkCount(const base::ListValue* args);
   void OnReportTimeToClickSink(const base::ListValue* args);
+  void OnReportWebUIRouteControllerLoaded(const base::ListValue* args);
   void OnReportTimeToInitialActionClose(const base::ListValue* args);
   void OnMediaControllerAvailable(const base::ListValue* args);
   void OnMediaControllerClosed(const base::ListValue* args);
   void OnSearchSinksAndCreateRoute(const base::ListValue* args);
+  void OnSelectLocalMediaFile(const base::ListValue* args);
   void OnInitialDataReceived(const base::ListValue* args);
 
   // Handlers for JavaScript messages to control the media.
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 9a03682c..8fee2ee 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -66,7 +66,6 @@
 const char kChromeUIInstantURL[] = "chrome://instant/";
 const char kChromeUIInterstitialURL[] = "chrome://interstitials/";
 const char kChromeUIInvalidationsURL[] = "chrome://invalidations/";
-const char kChromeUILargeIconURL[] = "chrome://large-icon/";
 const char kChromeUIMdPolicyURL[] = "chrome://md-policy/";
 const char kChromeUIMdSettingsURL[] = "chrome://md-settings/";
 const char kChromeUINaClURL[] = "chrome://nacl/";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index d422894..0e9d600 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -59,7 +59,6 @@
 extern const char kChromeUIInstantURL[];
 extern const char kChromeUIInterstitialURL[];
 extern const char kChromeUIInvalidationsURL[];
-extern const char kChromeUILargeIconURL[];
 extern const char kChromeUIMdPolicyURL[];
 extern const char kChromeUINaClURL[];
 extern const char kChromeUINetInternalsURL[];
diff --git a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
index 52c772c..c300974 100644
--- a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
@@ -212,7 +212,7 @@
 
   // Does the actual work of removing the iframe "frame1" from the document.
   void RemoveIframe() {
-    blink::WebFrame* main_frame = GetMainFrame();
+    blink::WebLocalFrame* main_frame = GetMainFrame();
     ASSERT_TRUE(main_frame);
     main_frame->ExecuteScript(blink::WebString(
         "document.body.removeChild(document.getElementById('frame1'));"));
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index f896cc8..2c9cac7 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -93,7 +93,7 @@
       v8::String::NewFromUtf8(isolate, "Invalid parameters")));
 }
 
-void Dispatch(blink::WebFrame* frame, const blink::WebString& script) {
+void Dispatch(blink::WebLocalFrame* frame, const blink::WebString& script) {
   if (!frame) return;
   frame->ExecuteScript(blink::WebScriptSource(script));
 }
@@ -442,7 +442,7 @@
 
 // static
 void SearchBoxExtension::DispatchChromeIdentityCheckResult(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     const base::string16& identity,
     bool identity_match) {
   std::string escaped_identity = base::GetQuotedJSONString(identity);
@@ -453,13 +453,13 @@
 }
 
 // static
-void SearchBoxExtension::DispatchFocusChange(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchFocusChange(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchFocusChangedScript);
 }
 
 // static
 void SearchBoxExtension::DispatchHistorySyncCheckResult(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     bool sync_history) {
   blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
       kDispatchHistorySyncCheckResult, sync_history ? "true" : "false")));
@@ -467,38 +467,38 @@
 }
 
 // static
-void SearchBoxExtension::DispatchInputCancel(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchInputCancel(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchInputCancelScript);
 }
 
 // static
-void SearchBoxExtension::DispatchInputStart(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchInputStart(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchInputStartScript);
 }
 
 // static
-void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchKeyCaptureChangeScript);
 }
 
 // static
 void SearchBoxExtension::DispatchMostVisitedChanged(
-    blink::WebFrame* frame) {
+    blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchMostVisitedChangedScript);
 }
 
 // static
-void SearchBoxExtension::DispatchSubmit(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchSubmit(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchSubmitEventScript);
 }
 
 // static
-void SearchBoxExtension::DispatchSuggestionChange(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchSuggestionChange(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchSuggestionChangeEventScript);
 }
 
 // static
-void SearchBoxExtension::DispatchThemeChange(blink::WebFrame* frame) {
+void SearchBoxExtension::DispatchThemeChange(blink::WebLocalFrame* frame) {
   Dispatch(frame, kDispatchThemeChangeEventScript);
 }
 
diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h
index 3f86ca02..87c38f2 100644
--- a/chrome/renderer/searchbox/searchbox_extension.h
+++ b/chrome/renderer/searchbox/searchbox_extension.h
@@ -13,7 +13,7 @@
 }
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace extensions_v8 {
@@ -27,19 +27,19 @@
   static v8::Extension* Get();
 
   // Helpers to dispatch Javascript events.
-  static void DispatchChromeIdentityCheckResult(blink::WebFrame* frame,
+  static void DispatchChromeIdentityCheckResult(blink::WebLocalFrame* frame,
                                                 const base::string16& identity,
                                                 bool identity_match);
-  static void DispatchFocusChange(blink::WebFrame* frame);
-  static void DispatchHistorySyncCheckResult(blink::WebFrame* frame,
+  static void DispatchFocusChange(blink::WebLocalFrame* frame);
+  static void DispatchHistorySyncCheckResult(blink::WebLocalFrame* frame,
                                              bool sync_history);
-  static void DispatchInputCancel(blink::WebFrame* frame);
-  static void DispatchInputStart(blink::WebFrame* frame);
-  static void DispatchKeyCaptureChange(blink::WebFrame* frame);
-  static void DispatchMostVisitedChanged(blink::WebFrame* frame);
-  static void DispatchSubmit(blink::WebFrame* frame);
-  static void DispatchSuggestionChange(blink::WebFrame* frame);
-  static void DispatchThemeChange(blink::WebFrame* frame);
+  static void DispatchInputCancel(blink::WebLocalFrame* frame);
+  static void DispatchInputStart(blink::WebLocalFrame* frame);
+  static void DispatchKeyCaptureChange(blink::WebLocalFrame* frame);
+  static void DispatchMostVisitedChanged(blink::WebLocalFrame* frame);
+  static void DispatchSubmit(blink::WebLocalFrame* frame);
+  static void DispatchSuggestionChange(blink::WebLocalFrame* frame);
+  static void DispatchThemeChange(blink::WebLocalFrame* frame);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(SearchBoxExtension);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 30c570a..eb23b9f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3748,6 +3748,7 @@
       "../browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc",
       "../browser/ui/webui/media_router/media_cast_mode_unittest.cc",
       "../browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc",
+      "../browser/ui/webui/media_router/media_router_file_dialog_unittest.cc",
       "../browser/ui/webui/media_router/media_router_ui_unittest.cc",
       "../browser/ui/webui/media_router/media_router_web_ui_test.cc",
       "../browser/ui/webui/media_router/media_router_web_ui_test.h",
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 73c21567..62290f2 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -17,7 +17,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/test_file_util.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/after_startup_task_utils.h"
diff --git a/chrome/test/data/webui/media_router/media_router_container_cast_mode_list_tests.js b/chrome/test/data/webui/media_router/media_router_container_cast_mode_list_tests.js
index a35e538..c8a02d5 100644
--- a/chrome/test/data/webui/media_router/media_router_container_cast_mode_list_tests.js
+++ b/chrome/test/data/webui/media_router/media_router_container_cast_mode_list_tests.js
@@ -64,6 +64,13 @@
       var fakeCastModeListWithPresentationModeForced = [];
 
       /**
+       * The list of CastModes including local media, which is just local file
+       * at the moement.
+       * @type {!Array<!media_router.CastMode>}
+       */
+      var fakeCastModeListWithLocalMedia = [];
+
+      /**
        * The blocking issue to show.
        * @type {?media_router.Issue}
        */
@@ -86,24 +93,30 @@
         PolymerTest.clearBody();
         // Initialize a media-router-container before each test.
         container = document.createElement('media-router-container');
+        container.get = function(strName) {
+          return this.$[strName];
+        };
+
         document.body.appendChild(container);
 
         // Get common functions and variables.
-        var test_base = media_router_container_test_base.init(container);
+        var testBase = media_router_container_test_base.init(container);
 
-        checkCurrentView = test_base.checkCurrentView;
-        checkElementsVisibleWithId = test_base.checkElementsVisibleWithId;
-        checkElementText = test_base.checkElementText;
-        fakeBlockingIssue = test_base.fakeBlockingIssue;
-        fakeCastModeList = test_base.fakeCastModeList;
+        checkCurrentView = testBase.checkCurrentView;
+        checkElementsVisibleWithId = testBase.checkElementsVisibleWithId;
+        checkElementText = testBase.checkElementText;
+        fakeBlockingIssue = testBase.fakeBlockingIssue;
+        fakeCastModeList = testBase.fakeCastModeList;
         fakeCastModeListWithNonPresentationModesOnly =
-            test_base.fakeCastModeListWithNonPresentationModesOnly;
+            testBase.fakeCastModeListWithNonPresentationModesOnly;
         fakeCastModeListWithPresentationModeForced =
-            test_base.fakeCastModeListWithPresentationModeForced;
-        fakeNonBlockingIssue = test_base.fakeNonBlockingIssue;
-        fakeSinkList = test_base.fakeSinkList;
+            testBase.fakeCastModeListWithPresentationModeForced;
+        fakeCastModeListWithLocalMedia =
+            testBase.fakeCastModeListWithLocalMedia;
+        fakeNonBlockingIssue = testBase.fakeNonBlockingIssue;
+        fakeSinkList = testBase.fakeSinkList;
 
-        container.castModeList = test_base.fakeCastModeList;
+        container.castModeList = testBase.fakeCastModeList;
 
         // Allow for the media router container to be created, attached, and
         // listeners registered in an afterNextRender() call.
@@ -134,14 +147,14 @@
       test('select cast mode', function(done) {
         container.castModeList = fakeCastModeListWithNonPresentationModesOnly;
 
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         checkCurrentView(media_router.MediaRouterView.CAST_MODE_LIST);
 
         setTimeout(function() {
           var castModeList =
               container.$$('#cast-mode-list').querySelectorAll('paper-item');
-          MockInteractions.tap(castModeList[2]);
+          MockInteractions.tap(castModeList[1]);
           checkCurrentView(media_router.MediaRouterView.SINK_LIST);
           done();
         });
@@ -152,12 +165,12 @@
       test('click drop down icon', function() {
         checkCurrentView(media_router.MediaRouterView.SINK_LIST);
 
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         checkCurrentView(media_router.MediaRouterView.CAST_MODE_LIST);
 
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         checkCurrentView(media_router.MediaRouterView.SINK_LIST);
       });
 
@@ -176,8 +189,8 @@
         container.castModeList = fakeCastModeListWithNonPresentationModesOnly;
 
         // Switch to cast mode list view.
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         setTimeout(function() {
           var castModeList =
               container.$$('#cast-mode-list').querySelectorAll('paper-item');
@@ -185,9 +198,11 @@
               castModeList.length);
           for (var i = 0; i < castModeList.length; i++) {
             MockInteractions.tap(castModeList[i]);
+
             assertEquals(
                 fakeCastModeListWithNonPresentationModesOnly[i].description,
                 container.headerText);
+
             checkElementText(
                 fakeCastModeListWithNonPresentationModesOnly[i].description,
                 castModeList[i]);
@@ -203,8 +218,8 @@
         container.castModeList = fakeCastModeList;
 
         // Switch to cast mode list view.
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         setTimeout(function() {
           var castModeList =
               container.$$('#cast-mode-list').querySelectorAll('paper-item');
@@ -266,8 +281,8 @@
             container.shownCastModeValue_);
         assertFalse(container.userHasSelectedCastMode_);
 
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         setTimeout(function() {
           var castModeList =
                 container.$$('#cast-mode-list')
@@ -278,7 +293,9 @@
                 container.headerText);
             assertEquals(fakeCastModeList[0].type,
                 container.shownCastModeValue_);
+
             assertTrue(container.userHasSelectedCastMode_);
+
             container.castModeList = fakeCastModeList.slice(1);
             setTimeout(function() {
               assertEquals(media_router.AUTO_CAST_MODE.description,
@@ -297,8 +314,8 @@
       test('changing cast mode changes sink list', function(done) {
         container.allSinks = fakeSinkList;
 
-        MockInteractions.tap(container.$['container-header'].
-            $['arrow-drop-icon']);
+        MockInteractions.tap(
+            container.get('container-header').$['arrow-drop-icon']);
         setTimeout(function() {
           var castModeList =
                 container.$$('#cast-mode-list')
@@ -395,8 +412,8 @@
           // Since we haven't selected a cast mode, we don't filter sinks.
           assertEquals(3, sinkList.length);
 
-          MockInteractions.tap(container.$['container-header'].
-              $['arrow-drop-icon']);
+          MockInteractions.tap(
+              container.get('container-header').$['arrow-drop-icon']);
           setTimeout(function() {
             // Cast mode 1 is selected, and the sink list is filtered.
             var castModeList =
@@ -445,6 +462,26 @@
         });
       });
 
+      // Tests that the header is set appropriately when files are selected
+      // one after the other.
+      test('cast to sink with existing route', function(done) {
+        container.castModeList = fakeCastModeListWithLocalMedia;
+
+        var fileName1 = 'file1';
+        var fileName2 = 'file2';
+
+        container.onFileDialogSuccess(fileName1);
+        setTimeout(function() {
+          assertTrue(container.headerText.includes(fileName1));
+          container.onFileDialogSuccess(fileName2);
+          setTimeout(function() {
+            assertTrue(container.headerText.includes(fileName2));
+            assertFalse(container.headerText.includes(fileName1));
+            done();
+          });
+        });
+      });
+
       // Tests that the 'cast' button is shown in the route details view when
       // the sink for the current route is compatible with the user-selected
       // cast mode.
@@ -466,8 +503,8 @@
               container.shadowRoot.getElementById('sink-list')
                   .querySelectorAll('paper-item');
 
-          MockInteractions.tap(container.$['container-header'].
-              $['arrow-drop-icon']);
+          MockInteractions.tap(
+              container.get('container-header').$['arrow-drop-icon']);
           setTimeout(function() {
             // Cast mode 1 is selected, and the sink list is filtered.
             var castModeList =
diff --git a/chrome/test/data/webui/media_router/media_router_container_test_base.js b/chrome/test/data/webui/media_router/media_router_container_test_base.js
index 18725f1..f702ce7e 100644
--- a/chrome/test/data/webui/media_router/media_router_container_test_base.js
+++ b/chrome/test/data/webui/media_router/media_router_container_test_base.js
@@ -85,9 +85,14 @@
      * @type {!Array<!media_router.CastMode>}
      */
     var fakeCastModeList = [
-      new media_router.CastMode(0x1, 'Cast google.com', 'google.com', false),
-      new media_router.CastMode(0x2, 'Description 1', null, false),
-      new media_router.CastMode(0x4, 'Description 2', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.PRESENTATION, 'Cast google.com',
+          'google.com', false),
+      new media_router.CastMode(
+          media_router.CastModeType.TAB_MIRROR, 'Description 1', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.DESKTOP_MIRROR, 'Description 2', null,
+          false),
     ];
 
     /**
@@ -95,9 +100,11 @@
      * @type {!Array<!media_router.CastMode>}
      */
     var fakeCastModeListWithNonPresentationModesOnly = [
-      new media_router.CastMode(0x2, 'Description 1', null, false),
-      new media_router.CastMode(0x4, 'Description 2', null, false),
-      new media_router.CastMode(0x8, 'Description 3', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.TAB_MIRROR, 'Description 1', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.DESKTOP_MIRROR, 'Description 2', null,
+          false),
     ];
 
     /**
@@ -105,9 +112,28 @@
      * @type {!Array<!media_router.CastMode>}
      */
     var fakeCastModeListWithPresentationModeForced = [
-      new media_router.CastMode(0x1, 'Cast google.com', 'google.com', true),
-      new media_router.CastMode(0x4, 'Description 2', null, false),
-      new media_router.CastMode(0x8, 'Description 3', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.PRESENTATION, 'Cast google.com',
+          'google.com', true),
+      new media_router.CastMode(
+          media_router.CastModeType.DESKTOP_MIRROR, 'Description 2', null,
+          false),
+      new media_router.CastMode(
+          media_router.CastModeType.LOCAL_FILE, 'Description 3', null, false),
+    ];
+
+    /**
+     * The list of CastModes to show with Local media on the list
+     * @type {!Array<!media_router.CastMode>}
+     */
+    var fakeCastModeListWithLocalMedia = [
+      new media_router.CastMode(
+          media_router.CastModeType.TAB_MIRROR, 'Description 1', null, false),
+      new media_router.CastMode(
+          media_router.CastModeType.DESKTOP_MIRROR, 'Description 2', null,
+          false),
+      new media_router.CastMode(
+          media_router.CastModeType.LOCAL_FILE, 'Description 3', null, false),
     ];
 
     /**
@@ -202,6 +228,7 @@
           fakeCastModeListWithNonPresentationModesOnly,
       fakeCastModeListWithPresentationModeForced:
           fakeCastModeListWithPresentationModeForced,
+      fakeCastModeListWithLocalMedia: fakeCastModeListWithLocalMedia,
       fakeNonBlockingIssue: fakeNonBlockingIssue,
       fakeRouteList: fakeRouteList,
       fakeRouteListWithLocalRoutesOnly: fakeRouteListWithLocalRoutesOnly,
diff --git a/chrome/test/data/webui/media_router/route_details_tests.js b/chrome/test/data/webui/media_router/route_details_tests.js
index f47a046..17451ad5 100644
--- a/chrome/test/data/webui/media_router/route_details_tests.js
+++ b/chrome/test/data/webui/media_router/route_details_tests.js
@@ -19,6 +19,13 @@
       var fakeRouteOne;
 
       /**
+       * The custom controller path for |fakeRouteOne|.
+       * @const @type {string}
+       */
+      var fakeRouteOneControllerPath =
+          'chrome-extension://123/custom_view.html';
+
+      /**
        * Second fake route created before each test.
        * @type {media_router.Route}
        */
@@ -84,9 +91,9 @@
         document.body.appendChild(details);
 
         // Initialize routes and sinks.
-        fakeRouteOne = new media_router.Route('route id 1', 'sink id 1',
-            'Video 1', 1, true, false,
-            'chrome-extension://123/custom_view.html');
+        fakeRouteOne = new media_router.Route(
+            'route id 1', 'sink id 1', 'Video 1', 1, true, false,
+            fakeRouteOneControllerPath);
         fakeRouteTwo = new media_router.Route('route id 2', 'sink id 2',
             'Video 2', 2, false, true);
         fakeSinkOne = new media_router.Sink(
@@ -205,7 +212,9 @@
         details.$$('extension-view-wrapper').$$('#custom-controller').load =
             function(url) {
           setTimeout(function() {
-            assertEquals('chrome-extension://123/custom_view.html', url);
+            assertEquals(
+                fakeRouteOneControllerPath,
+                url.substring(0, fakeRouteOneControllerPath.length));
             checkCustomControllerIsShown();
             done();
           });
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index ba3f58c4..78f20a0d 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -166,6 +166,14 @@
 #endif
 }
 
+bool CastContentRendererClient::IsSupportedBitstreamAudioCodec(
+    ::media::AudioCodec codec) {
+  return (codec == ::media::kCodecAC3 &&
+          media::MediaCapabilities::HdmiSinkSupportsAC3()) ||
+         (codec == ::media::kCodecEAC3 &&
+          media::MediaCapabilities::HdmiSinkSupportsEAC3());
+}
+
 blink::WebPrescientNetworking*
 CastContentRendererClient::GetPrescientNetworking() {
   return prescient_networking_dispatcher_.get();
diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h
index 74533aa..5a75c740 100644
--- a/chromecast/renderer/cast_content_renderer_client.h
+++ b/chromecast/renderer/cast_content_renderer_client.h
@@ -40,6 +40,7 @@
           key_systems_properties) override;
   bool IsSupportedAudioConfig(const ::media::AudioConfig& config) override;
   bool IsSupportedVideoConfig(const ::media::VideoConfig& config) override;
+  bool IsSupportedBitstreamAudioCodec(::media::AudioCodec codec) override;
   blink::WebPrescientNetworking* GetPrescientNetworking() override;
   void DeferMediaLoad(content::RenderFrame* render_frame,
                       bool render_frame_has_played_media_before,
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
index 3d3053c..c69959f 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -13,7 +13,6 @@
 #include "base/optional.h"
 #include "base/scoped_observer.h"
 #include "base/supports_user_data.h"
-#include "base/threading/non_thread_safe.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
 #include "components/sync/model/metadata_change_list.h"
diff --git a/components/component_updater/component_updater_service_unittest.cc b/components/component_updater/component_updater_service_unittest.cc
index 8cf3130..7d703c4 100644
--- a/components/component_updater/component_updater_service_unittest.cc
+++ b/components/component_updater/component_updater_service_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/component_updater/component_updater_service.h"
 
 #include <limits>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -47,9 +48,16 @@
  public:
   MockInstaller();
 
+  // gMock does not support mocking functions with parameters which have
+  // move semantics. This function is a shim to work around it.
+  Result Install(std::unique_ptr<base::DictionaryValue> manifest,
+                 const base::FilePath& unpack_path) {
+    return Install_(manifest, unpack_path);
+  }
+
   MOCK_METHOD1(OnUpdateError, void(int error));
-  MOCK_METHOD2(Install,
-               Result(const base::DictionaryValue& manifest,
+  MOCK_METHOD2(Install_,
+               Result(const std::unique_ptr<base::DictionaryValue>& manifest,
                       const base::FilePath& unpack_path));
   MOCK_METHOD2(GetInstalledFile,
                bool(const std::string& file, base::FilePath* installed_file));
diff --git a/components/component_updater/default_component_installer.cc b/components/component_updater/default_component_installer.cc
index 02e7f4e..340abf90 100644
--- a/components/component_updater/default_component_installer.cc
+++ b/components/component_updater/default_component_installer.cc
@@ -115,10 +115,11 @@
   return Result(InstallError::NONE);
 }
 
-Result DefaultComponentInstaller::Install(const base::DictionaryValue& manifest,
-                                          const base::FilePath& unpack_path) {
+Result DefaultComponentInstaller::Install(
+    std::unique_ptr<base::DictionaryValue> manifest,
+    const base::FilePath& unpack_path) {
   std::string manifest_version;
-  manifest.GetStringASCII("version", &manifest_version);
+  manifest->GetStringASCII("version", &manifest_version);
   base::Version version(manifest_version);
 
   VLOG(1) << "Install: version=" << version.GetString()
@@ -142,7 +143,7 @@
     if (!base::DeleteFile(install_path, true))
       return Result(InstallError::CLEAN_INSTALL_DIR_FAILED);
   }
-  const auto result = InstallHelper(manifest, unpack_path, install_path);
+  const auto result = InstallHelper(*manifest, unpack_path, install_path);
   if (result.error) {
     base::DeleteFile(install_path, true);
     return result;
@@ -154,13 +155,10 @@
   current_version_ = version;
   current_install_dir_ = install_path;
 
-  // TODO(ddorwin): Change parameter to std::unique_ptr<base::DictionaryValue>
-  // so we can avoid this DeepCopy.
-  std::unique_ptr<base::DictionaryValue> manifest_copy(manifest.DeepCopy());
   main_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&DefaultComponentInstaller::ComponentReady,
-                 this, base::Passed(&manifest_copy)));
+      FROM_HERE, base::Bind(&DefaultComponentInstaller::ComponentReady, this,
+                            base::Passed(std::move(manifest))));
+
   return result;
 }
 
diff --git a/components/component_updater/default_component_installer.h b/components/component_updater/default_component_installer.h
index 5ca9782..392186e 100644
--- a/components/component_updater/default_component_installer.h
+++ b/components/component_updater/default_component_installer.h
@@ -117,7 +117,7 @@
   // Overridden from ComponentInstaller:
   void OnUpdateError(int error) override;
   update_client::CrxInstaller::Result Install(
-      const base::DictionaryValue& manifest,
+      std::unique_ptr<base::DictionaryValue> manifest,
       const base::FilePath& unpack_path) override;
   bool GetInstalledFile(const std::string& file,
                         base::FilePath* installed_file) override;
diff --git a/components/component_updater/default_component_installer_unittest.cc b/components/component_updater/default_component_installer_unittest.cc
index 52fe601e..7078072 100644
--- a/components/component_updater/default_component_installer_unittest.cc
+++ b/components/component_updater/default_component_installer_unittest.cc
@@ -291,14 +291,14 @@
   const auto unpack_path = result().unpack_path;
   EXPECT_TRUE(base::DirectoryExists(unpack_path));
 
-  const auto manifest = update_client::ReadManifest(unpack_path);
+  auto manifest = update_client::ReadManifest(unpack_path);
 
   base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
   base::FilePath base_dir;
   EXPECT_TRUE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
   base_dir = base_dir.Append(relative_install_dir);
   EXPECT_TRUE(base::CreateDirectory(base_dir));
-  const auto result = installer->Install(*manifest, unpack_path);
+  const auto result = installer->Install(std::move(manifest), unpack_path);
   EXPECT_EQ(0, result.error);
   EXPECT_FALSE(base::PathExists(unpack_path));
 
@@ -315,7 +315,7 @@
   const auto unpack_path = result().unpack_path;
   EXPECT_TRUE(base::DirectoryExists(unpack_path));
 
-  const auto manifest = update_client::ReadManifest(unpack_path);
+  auto manifest = update_client::ReadManifest(unpack_path);
 
   // Test the precondition that DIR_COMPONENT_USER is not registered with
   // the path service.
@@ -323,7 +323,7 @@
   EXPECT_FALSE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
 
   // Calling |Install| fails since DIR_COMPONENT_USER does not exist.
-  const auto result = installer->Install(*manifest, unpack_path);
+  const auto result = installer->Install(std::move(manifest), unpack_path);
   EXPECT_EQ(
       static_cast<int>(update_client::InstallError::NO_DIR_COMPONENT_USER),
       result.error);
diff --git a/components/data_reduction_proxy/content/browser/content_lofi_decider.cc b/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
index 2261b4c..013475c4 100644
--- a/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
+++ b/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
@@ -73,7 +73,8 @@
 
   // Do not add the Chrome-Proxy-Accept-Transform header when the page load
   // explicitly forbids previews transformations.
-  if (previews_state & content::PREVIEWS_NO_TRANSFORM) {
+  if (previews_state & content::PREVIEWS_NO_TRANSFORM ||
+      previews_state & content::PREVIEWS_OFF) {
     return;
   }
 
diff --git a/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc b/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
index d594719..0a88e5e 100644
--- a/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
+++ b/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
@@ -251,8 +251,6 @@
       previews_state |= content::SERVER_LOFI_ON;
     if (tests[i].is_using_lite_page)
       previews_state |= content::SERVER_LITE_PAGE_ON;
-    if (previews_state == content::PREVIEWS_UNSPECIFIED)
-      previews_state = content::PREVIEWS_OFF;
 
     std::unique_ptr<net::URLRequest> request =
         CreateRequest(tests[i].is_main_frame, previews_state);
@@ -471,10 +469,10 @@
                {true, content::RESOURCE_TYPE_PLUGIN_RESOURCE}};
 
   for (size_t i = 0; i < arraysize(tests); ++i) {
-    std::unique_ptr<net::URLRequest> request =
-        CreateRequestByType(tests[i].resource_type, false,
-                            tests[i].is_using_lofi ? content::SERVER_LOFI_ON
-                                                   : content::PREVIEWS_OFF);
+    std::unique_ptr<net::URLRequest> request = CreateRequestByType(
+        tests[i].resource_type, false,
+        tests[i].is_using_lofi ? content::SERVER_LOFI_ON
+                               : content::PREVIEWS_UNSPECIFIED);
     net::HttpRequestHeaders headers;
     NotifyBeforeSendHeaders(&headers, request.get(), true);
     bool is_lofi_resource_type =
@@ -503,9 +501,10 @@
   } tests[] = {{false, false}, {false, true}, {true, false}, {true, true}};
 
   for (size_t i = 0; i < arraysize(tests); ++i) {
-    std::unique_ptr<net::URLRequest> request = CreateRequest(
-        tests[i].is_main_frame, tests[i].is_using_lofi ? content::SERVER_LOFI_ON
-                                                       : content::PREVIEWS_OFF);
+    std::unique_ptr<net::URLRequest> request =
+        CreateRequest(tests[i].is_main_frame,
+                      tests[i].is_using_lofi ? content::SERVER_LOFI_ON
+                                             : content::PREVIEWS_UNSPECIFIED);
     net::HttpRequestHeaders headers;
     NotifyBeforeSendHeaders(&headers, request.get(), true);
     VerifyLoFiHeader(false, false, headers);
@@ -533,10 +532,10 @@
   };
 
   for (size_t i = 0; i < arraysize(tests); ++i) {
-    std::unique_ptr<net::URLRequest> request =
-        CreateRequest(tests[i].is_main_frame, tests[i].is_using_lite_page
-                                                  ? content::SERVER_LITE_PAGE_ON
-                                                  : content::PREVIEWS_OFF);
+    std::unique_ptr<net::URLRequest> request = CreateRequest(
+        tests[i].is_main_frame, tests[i].is_using_lite_page
+                                    ? content::SERVER_LITE_PAGE_ON
+                                    : content::PREVIEWS_UNSPECIFIED);
     net::HttpRequestHeaders headers;
     NotifyBeforeSendHeaders(&headers, request.get(), true);
     VerifyLoFiHeader(false, false, headers);
@@ -573,8 +572,6 @@
       previews_state |= content::SERVER_LOFI_ON;
     if (tests[i].is_using_lite_page)
       previews_state |= content::SERVER_LITE_PAGE_ON;
-    if (previews_state == content::PREVIEWS_UNSPECIFIED)
-      previews_state = content::PREVIEWS_OFF;
 
     std::unique_ptr<net::URLRequest> request =
         CreateRequest(tests[i].is_main_frame, previews_state);
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 57e743e..b9ad7c4 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -15,8 +15,6 @@
     "favicon_usage_data.h",
     "favicon_util.cc",
     "favicon_util.h",
-    "large_icon_url_parser.cc",
-    "large_icon_url_parser.h",
     "select_favicon_frames.cc",
     "select_favicon_frames.h",
   ]
@@ -39,7 +37,6 @@
   testonly = true
   sources = [
     "favicon_url_parser_unittest.cc",
-    "large_icon_url_parser_unittest.cc",
     "select_favicon_frames_unittest.cc",
   ]
 
diff --git a/components/favicon_base/large_icon_url_parser.cc b/components/favicon_base/large_icon_url_parser.cc
deleted file mode 100644
index 9f1a9ac8..0000000
--- a/components/favicon_base/large_icon_url_parser.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/favicon_base/large_icon_url_parser.h"
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "third_party/skia/include/utils/SkParse.h"
-#include "ui/gfx/favicon_size.h"
-
-LargeIconUrlParser::LargeIconUrlParser() : size_in_pixels_(48) {
-}
-
-LargeIconUrlParser::~LargeIconUrlParser() {
-}
-
-bool LargeIconUrlParser::Parse(base::StringPiece path) {
-  if (path.empty())
-    return false;
-
-  size_t slash = path.find("/", 0);  // |path| does not start with '/'.
-  if (slash == base::StringPiece::npos)
-    return false;
-  base::StringPiece size_str = path.substr(0, slash);
-  // Disallow empty, non-numeric, or non-positive sizes.
-  if (size_str.empty() ||
-      !base::StringToInt(size_str, &size_in_pixels_) ||
-      size_in_pixels_ <= 0)
-    return false;
-
-  // Need to store the index of the URL field, so Instant Extended can translate
-  // large icon URLs using advanced parameters.
-  // Example:
-  //   "chrome-search://large-icon/48/<renderer-id>/<most-visited-id>"
-  // would be translated to:
-  //   "chrome-search://large-icon/48/<most-visited-item-with-given-id>".
-  path_index_ = slash + 1;
-  url_string_ = path.substr(path_index_).as_string();
-  return true;
-}
diff --git a/components/favicon_base/large_icon_url_parser.h b/components/favicon_base/large_icon_url_parser.h
deleted file mode 100644
index ca70c0d..0000000
--- a/components/favicon_base/large_icon_url_parser.h
+++ /dev/null
@@ -1,48 +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 COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
-#define COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-// A parser for parameters to the chrome://large-icon/ host.
-class LargeIconUrlParser {
- public:
-  LargeIconUrlParser();
-  ~LargeIconUrlParser();
-
-  std::string url_string() const { return url_string_; }
-
-  int size_in_pixels() const { return size_in_pixels_; }
-
-  size_t path_index() const { return path_index_; }
-
-  // Parses |path|, which should be in the format described at the top of the
-  // file "chrome/browser/ui/webui/large_icon_source.h". Note that |path| does
-  // not have leading '/'.
-  bool Parse(base::StringPiece path);
-
- private:
-  friend class LargeIconUrlParserTest;
-
-  // The page URL string of the requested large icon.
-  std::string url_string_;
-
-  // The size of the requested large icon in pixels.
-  int size_in_pixels_;
-
-  // The index of the first character (relative to the path) where the the URL
-  // from which the large icon is being requested is located.
-  size_t path_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(LargeIconUrlParser);
-};
-
-#endif  // COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
diff --git a/components/favicon_base/large_icon_url_parser_unittest.cc b/components/favicon_base/large_icon_url_parser_unittest.cc
deleted file mode 100644
index d58ca3f..0000000
--- a/components/favicon_base/large_icon_url_parser_unittest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/favicon_base/large_icon_url_parser.h"
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace {
-
-const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
-
-}  // namespace
-
-TEST(LargeIconUrlParserTest, ParseLargeIconPathSuccess) {
-  // Everything populated.
-  {
-    LargeIconUrlParser parser;
-    EXPECT_TRUE(parser.Parse(std::string("48/") + kTestUrlStr));
-    EXPECT_EQ(48, parser.size_in_pixels());
-    EXPECT_EQ(GURL(kTestUrlStr), GURL(parser.url_string()));
-    EXPECT_EQ(3U, parser.path_index());
-  }
-
-  // Empty URL.
-  {
-    LargeIconUrlParser parser;
-    EXPECT_TRUE(parser.Parse("48/"));
-    EXPECT_EQ(48, parser.size_in_pixels());
-    EXPECT_EQ(GURL(), GURL(parser.url_string()));
-    EXPECT_EQ(3U, parser.path_index());
-  }
-
-  // Tolerate invalid URL.
-  {
-    LargeIconUrlParser parser;
-    EXPECT_TRUE(parser.Parse("48/NOT A VALID URL"));
-    EXPECT_EQ(48, parser.size_in_pixels());
-    EXPECT_EQ("NOT A VALID URL", parser.url_string());
-    EXPECT_EQ(3U, parser.path_index());
-  }
-}
-
-TEST(LargeIconUrlParserTest, ParseLargeIconPathFailure) {
-  const char* const test_cases[] = {
-    "",
-    "/",
-    "//",
-    "48",  // Missing '/'.
-    "/http://www.google.com/",  // Missing size.
-    "not_a_number/http://www.google.com/",  // Bad size.
-    "0/http://www.google.com/",  // Non-positive size.
-    "-1/http://www.google.com/",  // Non-positive size.
-  };
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    LargeIconUrlParser parser;
-    EXPECT_FALSE(parser.Parse(test_cases[i])) << "test_cases[" << i << "]";
-  }
-}
diff --git a/components/feature_engagement_tracker/public/BUILD.gn b/components/feature_engagement_tracker/public/BUILD.gn
index 2bbd588..6d431bf 100644
--- a/components/feature_engagement_tracker/public/BUILD.gn
+++ b/components/feature_engagement_tracker/public/BUILD.gn
@@ -9,7 +9,6 @@
 
 source_set("public") {
   sources = [
-    "feature_constants.cc",
     "feature_constants.h",
     "feature_engagement_tracker.h",
     "feature_list.cc",
diff --git a/components/feature_engagement_tracker/public/feature_constants.cc b/components/feature_engagement_tracker/public/feature_constants.cc
deleted file mode 100644
index a15b54ee..0000000
--- a/components/feature_engagement_tracker/public/feature_constants.cc
+++ /dev/null
@@ -1,23 +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 "components/feature_engagement_tracker/public/feature_constants.h"
-
-#include "base/feature_list.h"
-
-namespace feature_engagement_tracker {
-
-const base::Feature kIPHDemoMode{"IPH_DemoMode",
-                                 base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kIPHDataSaverPreviewFeature{
-    "IPH_DataSaverPreview", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kIPHDataSaverDetailFeature{
-    "IPH_DataSaverDetail", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kIPHDownloadPageFeature{"IPH_DownloadPage",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kIPHDownloadHomeFeature{"IPH_DownloadHome",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
-}  // namespace feature_engagement_tracker
\ No newline at end of file
diff --git a/components/feature_engagement_tracker/public/feature_constants.h b/components/feature_engagement_tracker/public/feature_constants.h
index 705fa08..f340544 100644
--- a/components/feature_engagement_tracker/public/feature_constants.h
+++ b/components/feature_engagement_tracker/public/feature_constants.h
@@ -10,15 +10,23 @@
 namespace feature_engagement_tracker {
 
 // A feature for enabling a demonstration mode for In-Product Help.
-extern const base::Feature kIPHDemoMode;
+// This needs to be constexpr because of how it is used in
+// //chrome/browser/about_flags.cc.
+constexpr base::Feature kIPHDemoMode = {"IPH_DemoMode",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
 // All the features declared below should also be declared in the Java
 // version: org.chromium.components.feature_engagement_tracker.FeatureConstants.
-
-extern const base::Feature kIPHDataSaverPreviewFeature;
-extern const base::Feature kIPHDataSaverDetailFeature;
-extern const base::Feature kIPHDownloadPageFeature;
-extern const base::Feature kIPHDownloadHomeFeature;
+// These need to be constexpr, because they are used as
+// flags_ui::FeatureEntry::FeatureParams in feature_list.h.
+constexpr base::Feature kIPHDataSaverPreviewFeature = {
+    "IPH_DataSaverPreview", base::FEATURE_DISABLED_BY_DEFAULT};
+constexpr base::Feature kIPHDataSaverDetailFeature = {
+    "IPH_DataSaverDetail", base::FEATURE_DISABLED_BY_DEFAULT};
+constexpr base::Feature kIPHDownloadPageFeature = {
+    "IPH_DownloadPage", base::FEATURE_DISABLED_BY_DEFAULT};
+constexpr base::Feature kIPHDownloadHomeFeature = {
+    "IPH_DownloadHome", base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace feature_engagement_tracker
 
diff --git a/components/feature_engagement_tracker/public/feature_list.cc b/components/feature_engagement_tracker/public/feature_list.cc
index 3587f073..f8a920a 100644
--- a/components/feature_engagement_tracker/public/feature_list.cc
+++ b/components/feature_engagement_tracker/public/feature_list.cc
@@ -17,8 +17,8 @@
 // kFooFeature --> kFooFeatureVariation. This is intended to be used with
 // VARIATION_ENTRY below to be able to insert it into an array of
 // flags_ui::FeatureEntry::FeatureVariation.
-#define DEFINE_VARIATION_PARAM(base_feature)                               \
-  const flags_ui::FeatureEntry::FeatureParam base_feature##Variation[] = { \
+#define DEFINE_VARIATION_PARAM(base_feature)                                   \
+  constexpr flags_ui::FeatureEntry::FeatureParam base_feature##Variation[] = { \
       {kIPHDemoModeFeatureChoiceParam, base_feature.name}}
 
 // Defines a single flags_ui::FeatureEntry::FeatureVariation entry, fully
@@ -47,8 +47,8 @@
 
 const char kIPHDemoModeFeatureChoiceParam[] = "chosen_feature";
 
-const flags_ui::FeatureEntry::FeatureVariation kIPHDemoModeChoiceVariations[] =
-    {
+constexpr flags_ui::FeatureEntry::FeatureVariation
+    kIPHDemoModeChoiceVariations[] = {
         VARIATION_ENTRY(kIPHDataSaverPreviewFeature),
         VARIATION_ENTRY(kIPHDataSaverDetailFeature),
         VARIATION_ENTRY(kIPHDownloadPageFeature),
diff --git a/components/invalidation/impl/sync_system_resources.h b/components/invalidation/impl/sync_system_resources.h
index beb827b..b360086 100644
--- a/components/invalidation/impl/sync_system_resources.h
+++ b/components/invalidation/impl/sync_system_resources.h
@@ -17,7 +17,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/values.h"
 #include "components/invalidation/impl/state_writer.h"
 #include "components/invalidation/public/invalidation_export.h"
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc
index 9bd823b..0df175bd 100644
--- a/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -10,6 +10,7 @@
 #include <algorithm>
 #include <cstring>
 #include <map>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -55,7 +56,7 @@
 // with them.
 struct ProfilesState {
   ProfilesState(const CallStackProfileParams& params,
-                base::StackSamplingProfiler::CallStackProfiles profiles,
+                StackSamplingProfiler::CallStackProfiles profiles,
                 base::TimeTicks start_timestamp);
   ProfilesState(ProfilesState&&);
   ProfilesState& operator=(ProfilesState&&);
@@ -65,7 +66,7 @@
   CallStackProfileParams params;
 
   // The call stack profiles collected by the profiler.
-  base::StackSamplingProfiler::CallStackProfiles profiles;
+  StackSamplingProfiler::CallStackProfiles profiles;
 
   // The time at which the CallStackProfileMetricsProvider became aware of the
   // request for profiling. In particular, this is when callback was requested
@@ -77,10 +78,9 @@
   DISALLOW_COPY_AND_ASSIGN(ProfilesState);
 };
 
-ProfilesState::ProfilesState(
-    const CallStackProfileParams& params,
-    base::StackSamplingProfiler::CallStackProfiles profiles,
-    base::TimeTicks start_timestamp)
+ProfilesState::ProfilesState(const CallStackProfileParams& params,
+                             StackSamplingProfiler::CallStackProfiles profiles,
+                             base::TimeTicks start_timestamp)
     : params(params),
       profiles(std::move(profiles)),
       start_timestamp(start_timestamp) {}
@@ -284,47 +284,40 @@
   if (profile.samples.empty())
     return;
 
-  if (ordering_spec == CallStackProfileParams::PRESERVE_ORDER) {
-    // Collapse only consecutive repeated samples together.
-    CallStackProfile::Sample* current_sample_proto = nullptr;
-    uint32_t milestones = 0;
-    for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
-      // Check if the sample is different than the previous one. Samples match
+  const bool preserve_order =
+      (ordering_spec == CallStackProfileParams::PRESERVE_ORDER);
+
+  std::map<StackSamplingProfiler::Sample, int> sample_index;
+  uint32_t milestones = 0;
+  for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
+    int existing_sample_index = -1;
+    if (preserve_order) {
+      // Collapse sample with the previous one if they match. Samples match
       // if the frame and all annotations are the same.
-      if (!current_sample_proto || *it != *(it - 1)) {
-        current_sample_proto = proto_profile->add_sample();
-        CopySampleToProto(*it, profile.modules, current_sample_proto);
-        current_sample_proto->set_count(1);
-        CopyAnnotationsToProto(it->process_milestones & ~milestones,
-                               current_sample_proto);
-        milestones = it->process_milestones;
-      } else {
-        current_sample_proto->set_count(current_sample_proto->count() + 1);
-      }
-    }
-  } else {
-    // Collapse all repeated samples together.
-    std::map<StackSamplingProfiler::Sample, int> sample_index;
-    uint32_t milestones = 0;
-    for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
-      // Check for a sample already seen. Samples match if the frame and all
-      // annotations are the same.
+      if (proto_profile->sample_size() > 0 && *it == *(it - 1))
+        existing_sample_index = proto_profile->sample_size() - 1;
+    } else {
       auto location = sample_index.find(*it);
-      if (location == sample_index.end()) {
-        CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
-        CopySampleToProto(*it, profile.modules, sample_proto);
-        sample_proto->set_count(1);
-        CopyAnnotationsToProto(it->process_milestones & ~milestones,
-                               sample_proto);
-        sample_index.insert(
-            std::make_pair(
-                *it, static_cast<int>(proto_profile->sample().size()) - 1));
-        milestones = it->process_milestones;
-      } else {
-        CallStackProfile::Sample* sample_proto =
-            proto_profile->mutable_sample()->Mutable(location->second);
-        sample_proto->set_count(sample_proto->count() + 1);
-      }
+      if (location != sample_index.end())
+        existing_sample_index = location->second;
+    }
+
+    if (existing_sample_index != -1) {
+      CallStackProfile::Sample* sample_proto =
+          proto_profile->mutable_sample()->Mutable(existing_sample_index);
+      sample_proto->set_count(sample_proto->count() + 1);
+      continue;
+    }
+
+    CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
+    CopySampleToProto(*it, profile.modules, sample_proto);
+    sample_proto->set_count(1);
+    CopyAnnotationsToProto(it->process_milestones & ~milestones, sample_proto);
+    milestones = it->process_milestones;
+
+    if (!preserve_order) {
+      sample_index.insert(std::make_pair(
+          *it, static_cast<int>(proto_profile->sample_size()) - 1));
     }
   }
 
@@ -433,7 +426,7 @@
 }
 
 // This function can be invoked on an abitrary thread.
-base::StackSamplingProfiler::CompletedCallback
+StackSamplingProfiler::CompletedCallback
 CallStackProfileMetricsProvider::GetProfilerCallback(
     const CallStackProfileParams& params) {
   // Ignore the profiles if the collection is disabled. If the collection state
@@ -450,7 +443,7 @@
 void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
     const CallStackProfileParams& params,
     base::TimeTicks start_timestamp,
-    base::StackSamplingProfiler::CallStackProfiles profiles) {
+    StackSamplingProfiler::CallStackProfiles profiles) {
   ReceiveCompletedProfilesImpl(params, start_timestamp, std::move(profiles));
 }
 
diff --git a/components/payments/mojom/payment_app.mojom b/components/payments/mojom/payment_app.mojom
index dbf1a0f..f0bd4dd 100644
--- a/components/payments/mojom/payment_app.mojom
+++ b/components/payments/mojom/payment_app.mojom
@@ -38,7 +38,7 @@
       => (PaymentHandlerStatus status);
 };
 
-struct PaymentAppRequest {
+struct PaymentRequestEventData {
   url.mojom.Url top_level_origin;
   url.mojom.Url payment_request_origin;
   string payment_request_id;
diff --git a/components/plugins/renderer/loadable_plugin_placeholder.cc b/components/plugins/renderer/loadable_plugin_placeholder.cc
index 57678645..41ff1b0 100644
--- a/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -145,7 +145,7 @@
     return;
   std::string script =
       "window.setMessage(" + base::GetQuotedJSONString(message_) + ")";
-  plugin()->web_view()->MainFrame()->ExecuteScript(
+  plugin()->main_frame()->ExecuteScript(
       blink::WebScriptSource(blink::WebString::FromUTF8(script)));
 }
 
@@ -252,7 +252,7 @@
     std::string script = base::StringPrintf(
         "window.resizePoster('%dpx', '%dpx', '%dpx', '%dpx')", x, y, width,
         height);
-    plugin()->web_view()->MainFrame()->ExecuteScript(
+    plugin()->main_frame()->ExecuteScript(
         blink::WebScriptSource(blink::WebString::FromUTF8(script)));
   }
 }
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 80404cf..b5da7d5 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -68,7 +68,7 @@
                                      const GURL& url) {
   DCHECK(url.is_valid()) << "Blink requires the WebView to have a valid URL.";
   WebViewPlugin* plugin = new WebViewPlugin(render_view, delegate, preferences);
-  plugin->web_view()->MainFrame()->LoadHTMLString(html_data, url);
+  plugin->main_frame()->LoadHTMLString(html_data, url);
   return plugin;
 }
 
@@ -271,6 +271,13 @@
   web_view_->Close();
 }
 
+blink::WebLocalFrame* WebViewPlugin::WebViewHelper::main_frame() {
+  // WebViewHelper doesn't support OOPIFs so the main frame will
+  // always be local.
+  DCHECK(web_view_->MainFrame()->IsWebLocalFrame());
+  return static_cast<WebLocalFrame*>(web_view_->MainFrame());
+}
+
 bool WebViewPlugin::WebViewHelper::AcceptsLoadDrops() {
   return false;
 }
@@ -296,11 +303,7 @@
                                                  const WebImage&,
                                                  const WebPoint&) {
   // Immediately stop dragging.
-  DCHECK(web_view_->MainFrame()->IsWebLocalFrame());
-  web_view_->MainFrame()
-      ->ToWebLocalFrame()
-      ->FrameWidget()
-      ->DragSourceSystemDragEnded();
+  main_frame()->FrameWidget()->DragSourceSystemDragEnded();
 }
 
 bool WebViewPlugin::WebViewHelper::AllowsBrokenNullLayerTreeView() const {
@@ -345,8 +348,7 @@
 
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
-  v8::Local<v8::Context> context =
-      web_view_->MainFrame()->MainWorldScriptContext();
+  v8::Local<v8::Context> context = main_frame()->MainWorldScriptContext();
   DCHECK(!context.IsEmpty());
 
   v8::Context::Scope context_scope(context);
diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h
index 5e3693ea7..458296b1 100644
--- a/components/plugins/renderer/webview_plugin.h
+++ b/components/plugins/renderer/webview_plugin.h
@@ -20,6 +20,7 @@
 #include "third_party/WebKit/public/web/WebViewClient.h"
 
 namespace blink {
+class WebLocalFrame;
 class WebMouseEvent;
 }
 
@@ -68,7 +69,7 @@
                                const std::string& html_data,
                                const GURL& url);
 
-  blink::WebView* web_view() { return web_view_helper_.web_view(); }
+  blink::WebLocalFrame* main_frame() { return web_view_helper_.main_frame(); }
 
   const blink::WebString& old_title() const { return old_title_; }
 
@@ -116,6 +117,8 @@
                 const content::WebPreferences& preferences);
   ~WebViewPlugin() override;
 
+  blink::WebView* web_view() { return web_view_helper_.web_view(); }
+
   // content::RenderViewObserver methods:
   void OnDestruct() override {}
   void OnZoomLevelChanged() override;
@@ -151,6 +154,7 @@
     ~WebViewHelper() override;
 
     blink::WebView* web_view() { return web_view_; }
+    blink::WebLocalFrame* main_frame();
 
     // WebViewClient methods:
     bool AcceptsLoadDrops() override;
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index 528ca26..b9fa83a 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -99,7 +99,7 @@
 
 const char kPageSetupScriptFormat[] = "setup(%s);";
 
-void ExecuteScript(blink::WebFrame* frame,
+void ExecuteScript(blink::WebLocalFrame* frame,
                    const char* script_format,
                    const base::Value& parameters) {
   std::string json;
diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc
index f9663f3..709931a 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.cc
+++ b/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -333,10 +333,7 @@
   if (source != &menu_model_)
     return;
 
-  content::RenderWidgetHostView* view =
-      source_web_contents_->GetRenderWidgetHostView();
-  if (view)
-    view->SetShowingContextMenu(true);
+  source_web_contents_->SetShowingContextMenu(true);
 
   NotifyMenuShown();
 }
@@ -346,10 +343,7 @@
   if (source != &menu_model_)
     return;
 
-  content::RenderWidgetHostView* view =
-      source_web_contents_->GetRenderWidgetHostView();
-  if (view)
-    view->SetShowingContextMenu(false);
+  source_web_contents_->SetShowingContextMenu(false);
   source_web_contents_->NotifyContextMenuClosed(params_.custom_context);
 }
 
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index 1d84ced..d091d1b 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -245,6 +245,9 @@
 // Those core types that have high priority (includes ControlTypes()).
 ModelTypeSet PriorityCoreTypes();
 
+// Types that may commit data, but should never be included in a GetUpdates.
+ModelTypeSet CommitOnlyTypes();
+
 // Determine a model type from the field number of its associated
 // EntitySpecifics field.  Returns UNSPECIFIED if the field number is
 // not recognized.
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index d289eb41..d471c8c 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -539,6 +539,7 @@
 
   types_to_download.PutAll(clean_types);
   types_to_download.RemoveAll(ProxyTypes());
+  types_to_download.RemoveAll(CommitOnlyTypes());
   if (!types_to_download.Empty())
     types_to_download.Put(NIGORI);
 
diff --git a/components/sync/driver/sync_stopped_reporter_unittest.cc b/components/sync/driver/sync_stopped_reporter_unittest.cc
index e9d8789..e6b9d33 100644
--- a/components/sync/driver/sync_stopped_reporter_unittest.cc
+++ b/components/sync/driver/sync_stopped_reporter_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
-#include "base/threading/non_thread_safe.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
diff --git a/components/sync/engine_impl/cycle/nudge_tracker.cc b/components/sync/engine_impl/cycle/nudge_tracker.cc
index 15e83e1c..365b73c 100644
--- a/components/sync/engine_impl/cycle/nudge_tracker.cc
+++ b/components/sync/engine_impl/cycle/nudge_tracker.cc
@@ -24,6 +24,7 @@
                                        base::TimeDelta minimum_delay) {
   switch (model_type) {
     case AUTOFILL:
+    case USER_EVENTS:
       // Accompany types rely on nudges from other types, and hence have long
       // nudge delays.
       return base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index 3f0856f..9c9ca2b 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -290,7 +290,7 @@
 
   return base::MakeUnique<NonBlockingTypeCommitContribution>(
       model_type_state_.type_context(), commit_entities, this,
-      debug_info_emitter_);
+      debug_info_emitter_, CommitOnlyTypes().Has(GetModelType()));
 }
 
 void ModelTypeWorker::OnCommitResponse(CommitResponseDataList* response_list) {
@@ -312,7 +312,7 @@
 
     entity->ReceiveCommitResponse(&response);
 
-    if (is_deletion) {
+    if (is_deletion || CommitOnlyTypes().Has(GetModelType())) {
       entities_.erase(response.client_tag_hash);
     }
   }
@@ -346,8 +346,7 @@
 }
 
 bool ModelTypeWorker::IsTypeInitialized() const {
-  return model_type_state_.initial_sync_done() &&
-         !model_type_state_.progress_marker().token().empty();
+  return model_type_state_.initial_sync_done();
 }
 
 bool ModelTypeWorker::CanCommitItems() const {
@@ -397,6 +396,21 @@
   AddDefaultFieldValue(type_, sync_entity->mutable_specifics());
 
   // TODO(crbug.com/516866): Set parent_id_string for hierarchical types here.
+
+  if (CommitOnlyTypes().Has(GetModelType())) {
+    DCHECK(!cryptographer_);
+    // Remove absolutely everything we can get away with. We do not want to
+    // remove |client_defined_unique_tag| yet because the commit contribution
+    // needs the id to track the responses. They will remove it instead.
+    sync_entity->clear_attachment_id();
+    sync_entity->clear_ctime();
+    sync_entity->clear_deleted();
+    sync_entity->clear_folder();
+    sync_entity->clear_id_string();
+    sync_entity->clear_mtime();
+    sync_entity->clear_name();
+    sync_entity->clear_version();
+  }
 }
 
 void ModelTypeWorker::OnCryptographerUpdated() {
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index 201bd3c2..cfa93f8 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -101,6 +101,12 @@
   specifics->mutable_encrypted()->set_blob(encrypted);
 }
 
+void VerifyCommitCount(const NonBlockingTypeDebugInfoEmitter& emitter,
+                       int expected_count) {
+  EXPECT_EQ(expected_count, emitter.GetCommitCounters().num_commits_attempted);
+  EXPECT_EQ(expected_count, emitter.GetCommitCounters().num_commits_success);
+}
+
 }  // namespace
 
 // Tests the ModelTypeWorker.
@@ -136,9 +142,11 @@
       : foreign_encryption_key_index_(0),
         update_encryption_filter_index_(0),
         mock_type_processor_(nullptr),
-        mock_server_(kModelType),
+        mock_server_(base::MakeUnique<SingleTypeMockServer>(kModelType)),
         is_processor_disconnected_(false),
-        preferences_emitter_(kModelType, &type_observers_) {}
+        emitter_(base::MakeUnique<NonBlockingTypeDebugInfoEmitter>(
+            kModelType,
+            &type_observers_)) {}
 
   ~ModelTypeWorkerTest() override {}
 
@@ -153,7 +161,7 @@
     initial_state.mutable_progress_marker()->set_data_type_id(
         GetSpecificsFieldNumberFromModelType(kModelType));
 
-    InitializeWithState(initial_state, UpdateResponseDataList());
+    InitializeWithState(kModelType, initial_state, UpdateResponseDataList());
   }
 
   // Initializes with some existing data type state. Allows us to start
@@ -173,13 +181,26 @@
 
     initial_state.set_initial_sync_done(true);
 
-    InitializeWithState(initial_state, initial_pending_updates);
+    InitializeWithState(kModelType, initial_state, initial_pending_updates);
 
     mock_nudge_handler_.ClearCounters();
   }
 
+  void InitializeCommitOnly() {
+    mock_server_ = base::MakeUnique<SingleTypeMockServer>(USER_EVENTS);
+    emitter_ = base::MakeUnique<NonBlockingTypeDebugInfoEmitter>(
+        USER_EVENTS, &type_observers_);
+
+    // Don't set progress marker, commit only types don't use them.
+    ModelTypeState initial_state;
+    initial_state.set_initial_sync_done(true);
+
+    InitializeWithState(USER_EVENTS, initial_state, UpdateResponseDataList());
+  }
+
   // Initialize with a custom initial ModelTypeState and pending updates.
   void InitializeWithState(
+      const ModelType type,
       const ModelTypeState& state,
       const UpdateResponseDataList& initial_pending_updates) {
     DCHECK(!worker_);
@@ -197,9 +218,8 @@
 
     // TODO(maxbogue): crbug.com/529498: Inject pending updates somehow.
     worker_ = base::MakeUnique<ModelTypeWorker>(
-        kModelType, state, !state.initial_sync_done(),
-        std::move(cryptographer_copy), &mock_nudge_handler_,
-        std::move(processor), &preferences_emitter_);
+        type, state, !state.initial_sync_done(), std::move(cryptographer_copy),
+        &mock_nudge_handler_, std::move(processor), emitter_.get());
   }
 
   // Introduce a new key that the local cryptographer can't decrypt.
@@ -273,10 +293,13 @@
   // Modifications on the model thread that get sent to the worker under test.
 
   void CommitRequest(const std::string& name, const std::string& value) {
-    const std::string tag_hash = GenerateTagHash(name);
-    CommitRequestData data = mock_type_processor_->CommitRequest(
-        tag_hash, GenerateSpecifics(name, value));
-    worker_->EnqueueForCommit({data});
+    CommitRequest(GenerateTagHash(name), GenerateSpecifics(name, value));
+  }
+
+  void CommitRequest(const std::string& tag_hash,
+                     const EntitySpecifics& specifics) {
+    worker_->EnqueueForCommit(
+        {mock_type_processor_->CommitRequest(tag_hash, specifics)});
   }
 
   void DeleteRequest(const std::string& tag) {
@@ -288,9 +311,9 @@
   // Pretend to receive update messages from the server.
 
   void TriggerTypeRootUpdateFromServer() {
-    SyncEntity entity = mock_server_.TypeRootUpdate();
-    worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
-                                       mock_server_.GetContext(), {&entity},
+    SyncEntity entity = mock_server_->TypeRootUpdate();
+    worker_->ProcessGetUpdatesResponse(mock_server_->GetProgress(),
+                                       mock_server_->GetContext(), {&entity},
                                        nullptr);
     worker_->PassiveApplyUpdates(nullptr);
   }
@@ -298,7 +321,7 @@
   void TriggerPartialUpdateFromServer(int64_t version_offset,
                                       const std::string& tag,
                                       const std::string& value) {
-    SyncEntity entity = mock_server_.UpdateFromServer(
+    SyncEntity entity = mock_server_->UpdateFromServer(
         version_offset, GenerateTagHash(tag), GenerateSpecifics(tag, value));
 
     if (update_encryption_filter_index_ != 0) {
@@ -306,8 +329,8 @@
                     entity.mutable_specifics());
     }
 
-    worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
-                                       mock_server_.GetContext(), {&entity},
+    worker_->ProcessGetUpdatesResponse(mock_server_->GetProgress(),
+                                       mock_server_->GetContext(), {&entity},
                                        nullptr);
   }
 
@@ -321,15 +344,15 @@
   void TriggerTombstoneFromServer(int64_t version_offset,
                                   const std::string& tag) {
     SyncEntity entity =
-        mock_server_.TombstoneFromServer(version_offset, GenerateTagHash(tag));
+        mock_server_->TombstoneFromServer(version_offset, GenerateTagHash(tag));
 
     if (update_encryption_filter_index_ != 0) {
       EncryptUpdate(GetNthKeyParams(update_encryption_filter_index_),
                     entity.mutable_specifics());
     }
 
-    worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
-                                       mock_server_.GetContext(), {&entity},
+    worker_->ProcessGetUpdatesResponse(mock_server_->GetProgress(),
+                                       mock_server_->GetContext(), {&entity},
                                        nullptr);
     worker_->ApplyUpdates(nullptr);
   }
@@ -345,7 +368,7 @@
   // protocol. Try to use the other, higher level methods if possible.
   void DeliverRawUpdates(const SyncEntityList& list) {
     worker_->ProcessGetUpdatesResponse(
-        mock_server_.GetProgress(), mock_server_.GetContext(), list, nullptr);
+        mock_server_->GetProgress(), mock_server_->GetContext(), list, nullptr);
     worker_->ApplyUpdates(nullptr);
   }
 
@@ -389,7 +412,7 @@
     contribution->AddToCommitMessage(&message);
 
     sync_pb::ClientToServerResponse response =
-        mock_server_.DoSuccessfulCommit(message);
+        mock_server_->DoSuccessfulCommit(message);
 
     contribution->ProcessCommitResponse(response, nullptr);
     contribution->CleanUp();
@@ -416,7 +439,7 @@
   }
 
   // Returns the name of the encryption key in the cryptographer last passed to
-  // the CommitQueue. Returns an empty string if no crypgorapher is
+  // the CommitQueue. Returns an empty string if no cryptographer is
   // in use. See also: DecryptPendingKey().
   std::string GetLocalCryptographerKeyName() const {
     if (!cryptographer_) {
@@ -427,8 +450,8 @@
 
   MockModelTypeProcessor* processor() { return mock_type_processor_; }
   ModelTypeWorker* worker() { return worker_.get(); }
-  SingleTypeMockServer* server() { return &mock_server_; }
-  NonBlockingTypeDebugInfoEmitter* emitter() { return &preferences_emitter_; }
+  SingleTypeMockServer* server() { return mock_server_.get(); }
+  NonBlockingTypeDebugInfoEmitter* emitter() { return emitter_.get(); }
 
  private:
   // An encryptor for our cryptographer.
@@ -455,7 +478,7 @@
   // A mock that emulates enough of the sync server that it can be used
   // a single UpdateHandler and CommitContributor pair. In this test
   // harness, the |worker_| is both of them.
-  SingleTypeMockServer mock_server_;
+  std::unique_ptr<SingleTypeMockServer> mock_server_;
 
   // A mock to track the number of times the CommitQueue requests to
   // sync.
@@ -465,7 +488,7 @@
 
   base::ObserverList<TypeDebugInfoObserver> type_observers_;
 
-  NonBlockingTypeDebugInfoEmitter preferences_emitter_;
+  std::unique_ptr<NonBlockingTypeDebugInfoEmitter> emitter_;
 };
 
 // Requests a commit and verifies the messages sent to the client and server as
@@ -482,8 +505,7 @@
   EXPECT_FALSE(WillCommit());
   EXPECT_EQ(0U, server()->GetNumCommitMessages());
   EXPECT_EQ(0U, processor()->GetNumCommitResponses());
-  EXPECT_EQ(0, emitter()->GetCommitCounters().num_commits_attempted);
-  EXPECT_EQ(0, emitter()->GetCommitCounters().num_commits_success);
+  VerifyCommitCount(*emitter(), 0);
 
   CommitRequest(kTag1, kValue1);
 
@@ -509,9 +531,7 @@
   EXPECT_FALSE(entity.deleted());
   EXPECT_EQ(kValue1, entity.specifics().preference().value());
 
-  // Verify the counters update correctly.
-  EXPECT_EQ(1, emitter()->GetCommitCounters().num_commits_attempted);
-  EXPECT_EQ(1, emitter()->GetCommitCounters().num_commits_success);
+  VerifyCommitCount(*emitter(), 1);
 
   // Exhaustively verify the commit response returned to the model thread.
   ASSERT_EQ(1U, processor()->GetNumCommitResponses());
@@ -537,14 +557,11 @@
   // Step 1 is to create and commit a new entity.
   CommitRequest(kTag1, kValue1);
   EXPECT_EQ(1, GetNumCommitNudges());
-  EXPECT_EQ(0, emitter()->GetCommitCounters().num_commits_attempted);
-  EXPECT_EQ(0, emitter()->GetCommitCounters().num_commits_success);
+  VerifyCommitCount(*emitter(), 0);
   ASSERT_TRUE(WillCommit());
   DoSuccessfulCommit();
 
-  // Verify the counters update correctly.
-  EXPECT_EQ(1, emitter()->GetCommitCounters().num_commits_attempted);
-  EXPECT_EQ(1, emitter()->GetCommitCounters().num_commits_success);
+  VerifyCommitCount(*emitter(), 1);
 
   ASSERT_TRUE(processor()->HasCommitResponse(kHash1));
   const CommitResponseData& initial_commit_response =
@@ -556,9 +573,7 @@
   ASSERT_TRUE(WillCommit());
   DoSuccessfulCommit();
 
-  // Verify the counters update correctly.
-  EXPECT_EQ(2, emitter()->GetCommitCounters().num_commits_attempted);
-  EXPECT_EQ(2, emitter()->GetCommitCounters().num_commits_success);
+  VerifyCommitCount(*emitter(), 2);
 
   // Verify the SyncEntity sent in the commit message.
   ASSERT_EQ(2U, server()->GetNumCommitMessages());
@@ -694,7 +709,6 @@
   EXPECT_EQ(kTag1, entity.specifics.preference().name());
   EXPECT_EQ(kValue1, entity.specifics.preference().value());
 
-  // Verify the counters update correctly.
   EXPECT_EQ(1, emitter()->GetUpdateCounters().num_updates_received);
   EXPECT_EQ(1, emitter()->GetUpdateCounters().num_updates_applied);
 }
@@ -1172,4 +1186,44 @@
   }
 }
 
+TEST_F(ModelTypeWorkerTest, CommitOnly) {
+  InitializeCommitOnly();
+
+  int id = 123456789;
+  EntitySpecifics specifics;
+  specifics.mutable_user_event()->set_event_time_usec(id);
+  CommitRequest(kHash1, specifics);
+
+  EXPECT_EQ(1, GetNumCommitNudges());
+
+  ASSERT_TRUE(WillCommit());
+  DoSuccessfulCommit();
+
+  ASSERT_EQ(1U, server()->GetNumCommitMessages());
+  EXPECT_EQ(1, server()->GetNthCommitMessage(0).commit().entries_size());
+  const SyncEntity entity =
+      server()->GetNthCommitMessage(0).commit().entries(0);
+
+  EXPECT_EQ(0, entity.attachment_id_size());
+  EXPECT_FALSE(entity.has_ctime());
+  EXPECT_FALSE(entity.has_deleted());
+  EXPECT_FALSE(entity.has_folder());
+  EXPECT_FALSE(entity.has_id_string());
+  EXPECT_FALSE(entity.has_mtime());
+  EXPECT_FALSE(entity.has_version());
+  EXPECT_FALSE(entity.has_name());
+  EXPECT_TRUE(entity.specifics().has_user_event());
+  EXPECT_EQ(id, entity.specifics().user_event().event_time_usec());
+
+  VerifyCommitCount(*emitter(), 1);
+
+  ASSERT_EQ(1U, processor()->GetNumCommitResponses());
+  EXPECT_EQ(1U, processor()->GetNthCommitResponse(0).size());
+  ASSERT_TRUE(processor()->HasCommitResponse(kHash1));
+  const CommitResponseData& commit_response =
+      processor()->GetCommitResponse(kHash1);
+  EXPECT_EQ(kHash1, commit_response.client_tag_hash);
+  EXPECT_FALSE(commit_response.specifics_hash.empty());
+}
+
 }  // namespace syncer
diff --git a/components/sync/engine_impl/net/server_connection_manager.h b/components/sync/engine_impl/net/server_connection_manager.h
index 9f158233..052b51e 100644
--- a/components/sync/engine_impl/net/server_connection_manager.h
+++ b/components/sync/engine_impl/net/server_connection_manager.h
@@ -16,7 +16,6 @@
 #include "base/observer_list.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread_checker.h"
 #include "components/sync/base/cancelation_observer.h"
 #include "components/sync/syncable/syncable_id.h"
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
index 4a65be7..d6bb52f5 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
@@ -17,12 +17,14 @@
     const sync_pb::DataTypeContext& context,
     const google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>& entities,
     ModelTypeWorker* worker,
-    DataTypeDebugInfoEmitter* debug_info_emitter)
+    DataTypeDebugInfoEmitter* debug_info_emitter,
+    bool clear_client_defined_unique_tags)
     : worker_(worker),
       context_(context),
       entities_(entities),
       cleaned_up_(false),
-      debug_info_emitter_(debug_info_emitter) {}
+      debug_info_emitter_(debug_info_emitter),
+      clear_client_defined_unique_tags_(clear_client_defined_unique_tags) {}
 
 NonBlockingTypeCommitContribution::~NonBlockingTypeCommitContribution() {
   DCHECK(cleaned_up_);
@@ -33,8 +35,16 @@
   sync_pb::CommitMessage* commit_message = msg->mutable_commit();
   entries_start_index_ = commit_message->entries_size();
 
+  int startIndex = commit_message->entries_size();
   std::copy(entities_.begin(), entities_.end(),
             RepeatedPtrFieldBackInserter(commit_message->mutable_entries()));
+
+  if (clear_client_defined_unique_tags_) {
+    for (int i = startIndex; i < commit_message->entries_size(); ++i) {
+      commit_message->mutable_entries(i)->clear_client_defined_unique_tag();
+    }
+  }
+
   if (!context_.context().empty())
     commit_message->add_client_contexts()->CopyFrom(context_);
 
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution.h b/components/sync/engine_impl/non_blocking_type_commit_contribution.h
index 39d1f50..1f5aee8 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution.h
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution.h
@@ -29,7 +29,8 @@
       const sync_pb::DataTypeContext& context,
       const google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>& entities,
       ModelTypeWorker* worker,
-      DataTypeDebugInfoEmitter* debug_info_emitter);
+      DataTypeDebugInfoEmitter* debug_info_emitter,
+      bool clear_client_defined_unique_tags);
   ~NonBlockingTypeCommitContribution() override;
 
   // Implementation of CommitContribution
@@ -60,6 +61,13 @@
 
   DataTypeDebugInfoEmitter* debug_info_emitter_;
 
+  // If we should remove all the tag hashes from the commit data that actually
+  // gets sent over the wire. This is used to save bandwidth when we do not need
+  // these entities to have consistent client ids, such as with commit only
+  // types. These ids are still passed into this contribution object so that
+  // they can be set on response before handing back to the |worker_|.
+  bool clear_client_defined_unique_tags_;
+
   DISALLOW_COPY_AND_ASSIGN(NonBlockingTypeCommitContribution);
 };
 
diff --git a/components/sync/engine_impl/syncer.cc b/components/sync/engine_impl/syncer.cc
index ccdcb70..158652ce 100644
--- a/components/sync/engine_impl/syncer.cc
+++ b/components/sync/engine_impl/syncer.cc
@@ -114,14 +114,26 @@
                                      SyncCycle* cycle,
                                      const GetUpdatesDelegate& delegate,
                                      bool create_mobile_bookmarks_folder) {
+  // CommitOnlyTypes() should not be included in the GetUpdates, but should be
+  // included in the Commit. We are given a set of types for our SyncShare,
+  // and we must do this filtering. Note that |request_types| is also an out
+  // param, see below where we update it.
+  ModelTypeSet requested_commit_only_types =
+      Intersection(*request_types, CommitOnlyTypes());
+  ModelTypeSet download_types =
+      Difference(*request_types, requested_commit_only_types);
   GetUpdatesProcessor get_updates_processor(
       cycle->context()->model_type_registry()->update_handler_map(), delegate);
   SyncerError download_result = UNSET;
   do {
     download_result = get_updates_processor.DownloadUpdates(
-        request_types, cycle, create_mobile_bookmarks_folder);
+        &download_types, cycle, create_mobile_bookmarks_folder);
   } while (download_result == SERVER_MORE_TO_DOWNLOAD);
 
+  // It is our responsibility to propagate the removal of types that occurred in
+  // GetUpdatesProcessor::DownloadUpdates().
+  *request_types = Union(download_types, requested_commit_only_types);
+
   // Exit without applying if we're shutting down or an error was detected.
   if (download_result != SYNCER_OK || ExitRequested())
     return false;
@@ -135,7 +147,7 @@
     // Apply updates to the other types. May or may not involve cross-thread
     // traffic, depending on the underlying update handlers and the GU type's
     // delegate.
-    get_updates_processor.ApplyUpdates(*request_types,
+    get_updates_processor.ApplyUpdates(download_types,
                                        cycle->mutable_status_controller());
 
     cycle->context()->set_hierarchy_conflict_detected(
diff --git a/components/sync/engine_impl/syncer_unittest.cc b/components/sync/engine_impl/syncer_unittest.cc
index 7c230fe..170250c 100644
--- a/components/sync/engine_impl/syncer_unittest.cc
+++ b/components/sync/engine_impl/syncer_unittest.cc
@@ -5088,6 +5088,48 @@
   EXPECT_TRUE(directory()->InitialSyncEndedForType(PREFERENCES));
 }
 
+// Verify that commit only types are never requested in GetUpdates, but still
+// make it into the commit messages. Additionally, make sure failing GU types
+// are correctly removed before commit.
+TEST_F(SyncerTest, CommitOnlyTypes) {
+  mock_server_->set_partial_failure(true);
+  mock_server_->SetPartialFailureTypes(ModelTypeSet(PREFERENCES));
+
+  EnableDatatype(USER_EVENTS);
+  {
+    syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
+
+    MutableEntry pref(&trans, CREATE, PREFERENCES, ids_.root(), "name");
+    ASSERT_TRUE(pref.good());
+    pref.PutUniqueClientTag("tag1");
+    pref.PutIsUnsynced(true);
+
+    MutableEntry ext(&trans, CREATE, EXTENSIONS, ids_.root(), "name");
+    ASSERT_TRUE(ext.good());
+    ext.PutUniqueClientTag("tag2");
+    ext.PutIsUnsynced(true);
+
+    MutableEntry event(&trans, CREATE, USER_EVENTS, ids_.root(), "name");
+    ASSERT_TRUE(event.good());
+    event.PutUniqueClientTag("tag3");
+    event.PutIsUnsynced(true);
+  }
+
+  EXPECT_TRUE(SyncShareNudge());
+
+  ASSERT_EQ(2U, mock_server_->requests().size());
+  ASSERT_TRUE(mock_server_->requests()[0].has_get_updates());
+  // MockConnectionManager will ensure USER_EVENTS was not included in the GU.
+  EXPECT_EQ(
+      4, mock_server_->requests()[0].get_updates().from_progress_marker_size());
+
+  ASSERT_TRUE(mock_server_->requests()[1].has_commit());
+  const sync_pb::CommitMessage commit = mock_server_->requests()[1].commit();
+  EXPECT_EQ(2, commit.entries_size());
+  EXPECT_TRUE(commit.entries(0).specifics().has_extension());
+  EXPECT_TRUE(commit.entries(1).specifics().has_user_event());
+}
+
 // Tests specifically related to bookmark (and therefore no client tags) sync
 // logic. Entities without client tags have custom logic in parts of the code,
 // and hence are not covered by e.g. the Undeletion tests below.
diff --git a/components/sync/model/model_type_change_processor.cc b/components/sync/model/model_type_change_processor.cc
index f32b12fd..217300b 100644
--- a/components/sync/model/model_type_change_processor.cc
+++ b/components/sync/model/model_type_change_processor.cc
@@ -14,7 +14,8 @@
     const base::RepeatingClosure& dump_stack,
     ModelType type,
     ModelTypeSyncBridge* bridge) {
-  return base::MakeUnique<SharedModelTypeProcessor>(type, bridge, dump_stack);
+  return base::MakeUnique<SharedModelTypeProcessor>(
+      type, bridge, dump_stack, CommitOnlyTypes().Has(type));
 }
 
 ModelTypeChangeProcessor::ModelTypeChangeProcessor() {}
diff --git a/components/sync/model_impl/shared_model_type_processor.cc b/components/sync/model_impl/shared_model_type_processor.cc
index 322c6ca..6ac5136 100644
--- a/components/sync/model_impl/shared_model_type_processor.cc
+++ b/components/sync/model_impl/shared_model_type_processor.cc
@@ -25,10 +25,12 @@
 SharedModelTypeProcessor::SharedModelTypeProcessor(
     ModelType type,
     ModelTypeSyncBridge* bridge,
-    const base::RepeatingClosure& dump_stack)
+    const base::RepeatingClosure& dump_stack,
+    bool commit_only)
     : type_(type),
       bridge_(bridge),
       dump_stack_(dump_stack),
+      commit_only_(commit_only),
       weak_ptr_factory_(this) {
   DCHECK(bridge);
 }
@@ -306,34 +308,44 @@
     const sync_pb::ModelTypeState& type_state,
     const CommitResponseDataList& response_list) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::unique_ptr<MetadataChangeList> change_list =
+  std::unique_ptr<MetadataChangeList> metadata_change_list =
       bridge_->CreateMetadataChangeList();
+  EntityChangeList entity_change_list;
 
   model_type_state_ = type_state;
-  change_list->UpdateModelTypeState(model_type_state_);
+  metadata_change_list->UpdateModelTypeState(model_type_state_);
 
   for (const CommitResponseData& data : response_list) {
     ProcessorEntityTracker* entity = GetEntityForTagHash(data.client_tag_hash);
     if (entity == nullptr) {
       NOTREACHED() << "Received commit response for missing item."
-                   << " type: " << type_
+                   << " type: " << ModelTypeToString(type_)
                    << " client_tag_hash: " << data.client_tag_hash;
       continue;
     }
 
     entity->ReceiveCommitResponse(data);
 
-    if (entity->CanClearMetadata()) {
-      change_list->ClearMetadata(entity->storage_key());
+    if (commit_only_) {
+      if (!entity->IsUnsynced()) {
+        entity_change_list.push_back(
+            EntityChange::CreateDelete(entity->storage_key()));
+        metadata_change_list->ClearMetadata(entity->storage_key());
+        storage_key_to_tag_hash_.erase(entity->storage_key());
+        entities_.erase(entity->metadata().client_tag_hash());
+      }
+    } else if (entity->CanClearMetadata()) {
+      metadata_change_list->ClearMetadata(entity->storage_key());
       storage_key_to_tag_hash_.erase(entity->storage_key());
       entities_.erase(entity->metadata().client_tag_hash());
     } else {
-      change_list->UpdateMetadata(entity->storage_key(), entity->metadata());
+      metadata_change_list->UpdateMetadata(entity->storage_key(),
+                                           entity->metadata());
     }
   }
 
-  base::Optional<ModelError> error =
-      bridge_->ApplySyncChanges(std::move(change_list), EntityChangeList());
+  base::Optional<ModelError> error = bridge_->ApplySyncChanges(
+      std::move(metadata_change_list), entity_change_list);
   if (error) {
     ReportError(error.value());
   }
diff --git a/components/sync/model_impl/shared_model_type_processor.h b/components/sync/model_impl/shared_model_type_processor.h
index 17ac9e9..c64f800 100644
--- a/components/sync/model_impl/shared_model_type_processor.h
+++ b/components/sync/model_impl/shared_model_type_processor.h
@@ -40,7 +40,8 @@
  public:
   SharedModelTypeProcessor(ModelType type,
                            ModelTypeSyncBridge* bridge,
-                           const base::RepeatingClosure& dump_stack);
+                           const base::RepeatingClosure& dump_stack,
+                           bool commit_only);
   ~SharedModelTypeProcessor() override;
 
   // Whether the processor is allowing changes to its model type. If this is
@@ -218,6 +219,12 @@
   // MergeSyncData/ApplySyncChanges.
   std::map<std::string, std::string> storage_key_to_tag_hash_;
 
+  // If the processor should behave as if |type_| is one of the commit only
+  // model types. For this processor, being commit only means that on commit
+  // confirmation, we should delete local data, because the model side never
+  // intends to read it. This includes both data and metadata.
+  bool commit_only_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // WeakPtrFactory for this processor which will be sent to sync thread.
diff --git a/components/sync/model_impl/shared_model_type_processor_unittest.cc b/components/sync/model_impl/shared_model_type_processor_unittest.cc
index 7715747..7a3e1159 100644
--- a/components/sync/model_impl/shared_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/shared_model_type_processor_unittest.cc
@@ -59,15 +59,20 @@
   return FakeModelTypeSyncBridge::GenerateEntityData(key, value);
 }
 
+std::unique_ptr<ModelTypeChangeProcessor>
+CreateProcessor(bool commit_only, ModelType type, ModelTypeSyncBridge* bridge) {
+  return base::MakeUnique<SharedModelTypeProcessor>(
+      type, bridge, base::RepeatingClosure(), commit_only);
+}
+
 class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge {
  public:
-  TestModelTypeSyncBridge()
-      : FakeModelTypeSyncBridge(base::Bind(&ModelTypeChangeProcessor::Create,
-                                           base::RepeatingClosure())) {}
+  explicit TestModelTypeSyncBridge(bool commit_only)
+      : FakeModelTypeSyncBridge(base::Bind(&CreateProcessor, commit_only)) {}
 
-  explicit TestModelTypeSyncBridge(
-      std::unique_ptr<TestModelTypeSyncBridge> other)
-      : TestModelTypeSyncBridge() {
+  TestModelTypeSyncBridge(std::unique_ptr<TestModelTypeSyncBridge> other,
+                          bool commit_only)
+      : TestModelTypeSyncBridge(commit_only) {
     std::swap(db_, other->db_);
   }
 
@@ -174,7 +179,7 @@
 class SharedModelTypeProcessorTest : public ::testing::Test {
  public:
   SharedModelTypeProcessorTest()
-      : bridge_(base::MakeUnique<TestModelTypeSyncBridge>()) {}
+      : bridge_(base::MakeUnique<TestModelTypeSyncBridge>(false)) {}
 
   ~SharedModelTypeProcessorTest() override { CheckPostConditions(); }
 
@@ -220,10 +225,10 @@
     return specifics;
   }
 
-  void ResetState(bool keep_db) {
-    bridge_ =
-        keep_db ? base::MakeUnique<TestModelTypeSyncBridge>(std::move(bridge_))
-                : base::MakeUnique<TestModelTypeSyncBridge>();
+  void ResetState(bool keep_db, bool commit_only = false) {
+    bridge_ = keep_db ? base::MakeUnique<TestModelTypeSyncBridge>(
+                            std::move(bridge_), commit_only)
+                      : base::MakeUnique<TestModelTypeSyncBridge>(commit_only);
     worker_ = nullptr;
     CheckPostConditions();
   }
@@ -691,6 +696,48 @@
   EXPECT_EQ(1, acked_metadata.server_version());
 }
 
+// Test that commit only types are deleted after commit response.
+TEST_F(SharedModelTypeProcessorTest, CommitOnlySimple) {
+  ResetState(false, true);
+  InitializeToReadyState();
+
+  bridge()->WriteItem(kKey1, kValue1);
+  EXPECT_EQ(1U, db().data_count());
+  EXPECT_EQ(1U, db().metadata_count());
+
+  worker()->VerifyPendingCommits({kHash1});
+  worker()->AckOnePendingCommit();
+  EXPECT_EQ(0U, db().data_count());
+  EXPECT_EQ(0U, db().metadata_count());
+}
+
+// Test that commit only types maintain tracking of entities while unsynced
+// changes exist.
+TEST_F(SharedModelTypeProcessorTest, CommitOnlyUnsyncedChanges) {
+  ResetState(false, true);
+  InitializeToReadyState();
+
+  bridge()->WriteItem(kKey1, kValue1);
+  worker()->VerifyPendingCommits({kHash1});
+  EXPECT_EQ(1U, db().data_count());
+  EXPECT_EQ(1U, db().metadata_count());
+
+  bridge()->WriteItem(kKey1, kValue2);
+  worker()->VerifyPendingCommits({kHash1, kHash1});
+  EXPECT_EQ(1U, db().data_count());
+  EXPECT_EQ(1U, db().metadata_count());
+
+  worker()->AckOnePendingCommit();
+  worker()->VerifyPendingCommits({kHash1});
+  EXPECT_EQ(1U, db().data_count());
+  EXPECT_EQ(1U, db().metadata_count());
+
+  worker()->AckOnePendingCommit();
+  worker()->VerifyPendingCommits(std::vector<std::string>());
+  EXPECT_EQ(0U, db().data_count());
+  EXPECT_EQ(0U, db().metadata_count());
+}
+
 // Test that an error applying metadata changes from a commit response is
 // propagated to the error handler.
 TEST_F(SharedModelTypeProcessorTest, ErrorApplyingAck) {
diff --git a/components/sync/syncable/model_type.cc b/components/sync/syncable/model_type.cc
index 20094c7..0a924fd 100644
--- a/components/sync/syncable/model_type.cc
+++ b/components/sync/syncable/model_type.cc
@@ -541,6 +541,10 @@
   return result;
 }
 
+ModelTypeSet CommitOnlyTypes() {
+  return ModelTypeSet(USER_EVENTS);
+}
+
 const char* ModelTypeToString(ModelType model_type) {
   // This is used in serialization routines as well as for displaying debug
   // information.  Do not attempt to change these string values unless you know
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc
index 64952a54..19b8879 100644
--- a/components/sync/test/engine/mock_connection_manager.cc
+++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -549,9 +549,12 @@
   std::string token = response->get_updates().new_progress_marker(0).token();
   response->mutable_get_updates()->clear_new_progress_marker();
   for (int i = 0; i < gu.from_progress_marker_size(); ++i) {
+    int data_type_id = gu.from_progress_marker(i).data_type_id();
+    EXPECT_TRUE(expected_filter_.Has(
+        GetModelTypeFromSpecificsFieldNumber(data_type_id)));
     sync_pb::DataTypeProgressMarker* new_marker =
         response->mutable_get_updates()->add_new_progress_marker();
-    new_marker->set_data_type_id(gu.from_progress_marker(i).data_type_id());
+    new_marker->set_data_type_id(data_type_id);
     new_marker->set_token(token);
   }
 
diff --git a/components/sync/user_events/user_event_sync_bridge.cc b/components/sync/user_events/user_event_sync_bridge.cc
index 51e058a..f601791 100644
--- a/components/sync/user_events/user_event_sync_bridge.cc
+++ b/components/sync/user_events/user_event_sync_bridge.cc
@@ -84,7 +84,15 @@
 base::Optional<ModelError> UserEventSyncBridge::ApplySyncChanges(
     std::unique_ptr<MetadataChangeList> metadata_change_list,
     EntityChangeList entity_changes) {
-  NOTREACHED();
+  std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
+  for (EntityChange& change : entity_changes) {
+    DCHECK_EQ(EntityChange::ACTION_DELETE, change.type());
+    batch->DeleteData(change.storage_key());
+  }
+  batch->TransferMetadataChanges(std::move(metadata_change_list));
+  store_->CommitWriteBatch(
+      std::move(batch),
+      base::Bind(&UserEventSyncBridge::OnCommit, base::AsWeakPtr(this)));
   return {};
 }
 
diff --git a/components/sync/user_events/user_event_sync_bridge_unittest.cc b/components/sync/user_events/user_event_sync_bridge_unittest.cc
index 31b81f60..33443cc 100644
--- a/components/sync/user_events/user_event_sync_bridge_unittest.cc
+++ b/components/sync/user_events/user_event_sync_bridge_unittest.cc
@@ -128,6 +128,19 @@
   bridge()->GetAllData(base::Bind(&VerifyDataBatchCount, 4));
 }
 
+TEST_F(UserEventSyncBridgeTest, ApplySyncChanges) {
+  bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 1u));
+  bridge()->RecordUserEvent(SpecificsUniquePtr(2u, 2u, 2u));
+  bridge()->GetAllData(base::Bind(&VerifyDataBatchCount, 2));
+
+  const std::string storage_key = processor().put_multimap().begin()->first;
+  auto error_on_delete =
+      bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                                 {EntityChange::CreateDelete(storage_key)});
+  EXPECT_FALSE(error_on_delete);
+  bridge()->GetAllData(base::Bind(&VerifyDataBatchCount, 1));
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/update_client/component.cc b/components/update_client/component.cc
index a63b5cd..ea94148 100644
--- a/components/update_client/component.cc
+++ b/components/update_client/component.cc
@@ -83,7 +83,7 @@
   if (!manifest)
     return CrxInstaller::Result(InstallError::BAD_MANIFEST);
 
-  return installer->Install(*manifest, unpack_path);
+  return installer->Install(std::move(manifest), unpack_path);
 }
 
 void InstallOnBlockingTaskRunner(
diff --git a/components/update_client/test_installer.cc b/components/update_client/test_installer.cc
index 7a003dbf..795dd41 100644
--- a/components/update_client/test_installer.cc
+++ b/components/update_client/test_installer.cc
@@ -30,7 +30,7 @@
 }
 
 CrxInstaller::Result TestInstaller::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
   ++install_count_;
 
@@ -70,10 +70,10 @@
 }
 
 CrxInstaller::Result VersionedTestInstaller::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
   std::string version_string;
-  manifest.GetStringASCII("version", &version_string);
+  manifest->GetStringASCII("version", &version_string);
   base::Version version(version_string.c_str());
 
   base::FilePath path;
diff --git a/components/update_client/test_installer.h b/components/update_client/test_installer.h
index 473772e..3fc810ad 100644
--- a/components/update_client/test_installer.h
+++ b/components/update_client/test_installer.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
 #define COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
 
+#include <memory>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -25,7 +26,7 @@
 
   void OnUpdateError(int error) override;
 
-  Result Install(const base::DictionaryValue& manifest,
+  Result Install(std::unique_ptr<base::DictionaryValue> manifest,
                  const base::FilePath& unpack_path) override;
 
   bool GetInstalledFile(const std::string& file,
@@ -69,7 +70,7 @@
  public:
   VersionedTestInstaller();
 
-  Result Install(const base::DictionaryValue& manifest,
+  Result Install(std::unique_ptr<base::DictionaryValue> manifest,
                  const base::FilePath& unpack_path) override;
 
   bool GetInstalledFile(const std::string& file,
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h
index d068a56..18b540a 100644
--- a/components/update_client/update_client.h
+++ b/components/update_client/update_client.h
@@ -185,7 +185,7 @@
   // as a json dictionary.|unpack_path| contains the temporary directory
   // with all the unpacked CRX files.
   // This method may be called from a thread other than the main thread.
-  virtual Result Install(const base::DictionaryValue& manifest,
+  virtual Result Install(std::unique_ptr<base::DictionaryValue> manifest,
                          const base::FilePath& unpack_path) = 0;
 
   // Sets |installed_file| to the full path to the installed |file|. |file| is
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc
index fae978c..b9386eb 100644
--- a/components/update_client/update_client_unittest.cc
+++ b/components/update_client/update_client_unittest.cc
@@ -1362,15 +1362,22 @@
 TEST_F(UpdateClientTest, OneCrxInstallError) {
   class MockInstaller : public CrxInstaller {
    public:
+    // gMock does not support mocking functions with parameters which have
+    // move semantics. This function is a shim to work around it.
+    Result Install(std::unique_ptr<base::DictionaryValue> manifest,
+                   const base::FilePath& unpack_path) {
+      return Install_(manifest, unpack_path);
+    }
+
     MOCK_METHOD1(OnUpdateError, void(int error));
-    MOCK_METHOD2(Install,
-                 Result(const base::DictionaryValue& manifest,
+    MOCK_METHOD2(Install_,
+                 Result(const std::unique_ptr<base::DictionaryValue>& manifest,
                         const base::FilePath& unpack_path));
     MOCK_METHOD2(GetInstalledFile,
                  bool(const std::string& file, base::FilePath* installed_file));
     MOCK_METHOD0(Uninstall, bool());
 
-    void OnInstall(const base::DictionaryValue& manifest,
+    void OnInstall(const std::unique_ptr<base::DictionaryValue>& manifest,
                    const base::FilePath& unpack_path) {
       unpack_path_ = unpack_path;
       EXPECT_TRUE(base::DirectoryExists(unpack_path_));
@@ -1398,7 +1405,7 @@
           base::MakeRefCounted<MockInstaller>();
 
       EXPECT_CALL(*installer, OnUpdateError(_)).Times(0);
-      EXPECT_CALL(*installer, Install(_, _))
+      EXPECT_CALL(*installer, Install_(_, _))
           .WillOnce(
               DoAll(Invoke(installer.get(), &MockInstaller::OnInstall),
                     Return(CrxInstaller::Result(InstallError::GENERIC_ERROR))));
diff --git a/components/update_client/utils.h b/components/update_client/utils.h
index 02c9925..7655ffb 100644
--- a/components/update_client/utils.h
+++ b/components/update_client/utils.h
@@ -85,7 +85,7 @@
 void RemoveUnsecureUrls(std::vector<GURL>* urls);
 
 // Adapter function for the old definitions of CrxInstaller::Install until the
-// component installer code is migrated to use a REsult instead of bool.
+// component installer code is migrated to use a Result instead of bool.
 CrxInstaller::Result InstallFunctionWrapper(base::Callback<bool()> callback);
 
 }  // namespace update_client
diff --git a/content/browser/compositor/browser_compositor_output_surface.h b/content/browser/compositor/browser_compositor_output_surface.h
index f7c6f82..84d56910d1 100644
--- a/content/browser/compositor/browser_compositor_output_surface.h
+++ b/content/browser/compositor/browser_compositor_output_surface.h
@@ -6,7 +6,6 @@
 #define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
 
 #include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
 #include "build/build_config.h"
 #include "cc/output/output_surface.h"
 #include "content/common/content_export.h"
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc
index b3dffc71..0cdfc5e 100644
--- a/content/browser/media/media_browsertest.cc
+++ b/content/browser/media/media_browsertest.cc
@@ -36,7 +36,6 @@
 
 void MediaBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
   command_line->AppendSwitch(switches::kIgnoreAutoplayRestrictionsForTests);
-  command_line->AppendSwitch(switches::kEnableVp9InMp4);
 }
 
 void MediaBrowserTest::RunMediaTestPage(const std::string& html_page,
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc
index 3235bdf..f9e859b 100644
--- a/content/browser/media/media_canplaytype_browsertest.cc
+++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -851,7 +851,6 @@
   EXPECT_EQ(kHevcSupported,
             CanPlay("'video/mp4; codecs=\"hvc1.1.6.L93.B0, mp4a.40.5\"'"));
 
-  // switches::kEnableVp9InMp4 is enabled in MediaBrowserTest.
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"vp09.00.10.08\"'"));
 
   TestMPEGUnacceptableCombinations("video/mp4");
@@ -1533,24 +1532,19 @@
 
   // Platform support is sadly ambiguous for profiles > 0.
   // TODO(chcunningham): Plumb proper querying of platform support - give a firm
-  // answer.
+  // answer. See https://crbug.com/604566.
   EXPECT_EQ(new_vp9_maybe, CanPlay(mime_prefix + "codecs=\"vp09.01.10.08\"'"));
   EXPECT_EQ(new_vp9_maybe, CanPlay(mime_prefix + "codecs=\"vp09.02.10.08\"'"));
   EXPECT_EQ(new_vp9_maybe, CanPlay(mime_prefix + "codecs=\"vp09.03.10.08\"'"));
 }
 
 const CanPlayTypeNewVp9Params kNewVp9ParamVariants[] = {
-    // Expect CanPlay(...) = kNotEmpty when command line flag empty.
-    {"", "video/mp4", kNot, kNot},
+    // Expect CanPlay(...) = kProbably/kMaybe for MP4 as MP4 supports new style
+    // codec string unconditionally.
+    {"", "video/mp4", kPropProbably, kPropMaybe},
+    // Expect CanPlay(...) = kProbably/kMaybe for WebM, only if the relevant
+    // flags are enabled..
     {"", "video/webm", kNot, kNot},
-    // Expect CanPlay(...) = kProbably/kMaybe for MP4, but not for WebM for
-    // these command line flags.
-    {switches::kEnableVp9InMp4, "video/mp4", kPropProbably, kPropMaybe},
-    {switches::kEnableVp9InMp4, "video/webm", kNot, kNot},
-    // Expect CanPlay(...) = kProbably/kMaybe for WebM, but not for MP4 for
-    // these command line flags.
-    {switches::kEnableHDR, "video/mp4", kNot, kNot},
-    {switches::kEnableNewVp9CodecString, "video/mp4", kNot, kNot},
     {switches::kEnableHDR, "video/webm", kProbably, kMaybe},
     {switches::kEnableNewVp9CodecString, "video/webm", kProbably, kMaybe},
 };
diff --git a/content/browser/media/media_capabilities_browsertest.cc b/content/browser/media/media_capabilities_browsertest.cc
index ab5e639..2dd5286 100644
--- a/content/browser/media/media_capabilities_browsertest.cc
+++ b/content/browser/media/media_capabilities_browsertest.cc
@@ -42,7 +42,6 @@
     command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
                                     "MediaCapabilities");
     command_line->AppendSwitch(switches::kEnableNewVp9CodecString);
-    command_line->AppendSwitch(switches::kEnableVp9InMp4);
   }
 
   std::string CanDecodeAudio(const std::string& content_type) {
@@ -104,7 +103,6 @@
             CanDecodeVideo("'video/mp4; codecs=\"avc1.42701E\"'"));
   EXPECT_EQ(kPropSupported,
             CanDecodeVideo("'video/mp4; codecs=\"avc1.42F01E\"'"));
-  // Requires command line flag switches::kEnableVp9InMp4
   EXPECT_EQ(kPropSupported,
             CanDecodeVideo("'video/mp4; codecs=\"vp09.00.10.08\"'"));
 
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 3828cf0..a455f44 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -367,7 +367,40 @@
     RecordWatchTime(key, value, true);
   }
 
-  enum class FinalizeType { EVERYTHING, POWER_ONLY };
+  void RecordWatchTimeWithFilter(
+      const GURL& url,
+      WatchTimeInfo* watch_time_info,
+      const base::flat_set<base::StringPiece>& watch_time_filter) {
+    // UKM may be unavailable in content_shell or other non-chrome/ builds; it
+    // may also be unavailable if browser shutdown has started.
+    const bool has_ukm = !!ukm::UkmRecorder::Get();
+    std::unique_ptr<ukm::UkmEntryBuilder> builder;
+
+    for (auto it = watch_time_info->begin(); it != watch_time_info->end();) {
+      if (!watch_time_filter.empty() &&
+          watch_time_filter.find(it->first) == watch_time_filter.end()) {
+        ++it;
+        continue;
+      }
+
+      RecordWatchTime(it->first, it->second);
+
+      if (has_ukm && ShouldReportUkmWatchTime(it->first)) {
+        if (!builder)
+          builder = media_internals_->CreateUkmBuilder(url, kWatchTimeEvent);
+
+        // Strip Media.WatchTime. prefix for UKM since they're already
+        // grouped; arraysize() includes \0, so no +1 necessary for trailing
+        // period.
+        builder->AddMetric(it->first.substr(arraysize(kWatchTimeEvent)).data(),
+                           it->second.InMilliseconds());
+      }
+
+      it = watch_time_info->erase(it);
+    }
+  }
+
+  enum class FinalizeType { EVERYTHING, POWER_ONLY, CONTROLS_ONLY };
   void FinalizeWatchTime(bool has_video,
                          const GURL& url,
                          int* underflow_count,
@@ -378,64 +411,32 @@
     if (!url.is_valid())
       return;
 
-    // UKM may be unavailable in content_shell or other non-chrome/ builds; it
-    // may also be unavailable if browser shutdown has started.
-    const bool has_ukm = !!ukm::UkmRecorder::Get();
+    switch (finalize_type) {
+      case FinalizeType::EVERYTHING:
+        if (*underflow_count) {
+          // Check for watch times entries that have corresponding MTBR entries
+          // and report the MTBR value using watch_time / |underflow_count|
+          for (auto& kv : mtbr_keys_) {
+            auto it = watch_time_info->find(kv.first);
+            if (it == watch_time_info->end())
+              continue;
+            RecordMeanTimeBetweenRebuffers(kv.second,
+                                           it->second / *underflow_count);
+          }
 
-    if (finalize_type == FinalizeType::EVERYTHING) {
-      if (*underflow_count) {
-        // Check for watch times entries that have corresponding MTBR entries
-        // and report the MTBR value using watch_time / |underflow_count|
-        for (auto& kv : mtbr_keys_) {
-          auto it = watch_time_info->find(kv.first);
-          if (it == watch_time_info->end())
-            continue;
-          RecordMeanTimeBetweenRebuffers(kv.second,
-                                         it->second / *underflow_count);
+          *underflow_count = 0;
         }
 
-        *underflow_count = 0;
-      }
-
-      std::unique_ptr<ukm::UkmEntryBuilder> builder;
-      for (auto& kv : *watch_time_info) {
-        RecordWatchTime(kv.first, kv.second);
-
-        if (!has_ukm || !ShouldReportUkmWatchTime(kv.first))
-          continue;
-
-        if (!builder)
-          builder = media_internals_->CreateUkmBuilder(url, kWatchTimeEvent);
-
-        // Strip Media.WatchTime. prefix for UKM since they're already grouped;
-        // arraysize() includes \0, so no +1 necessary for trailing period.
-        builder->AddMetric(kv.first.substr(arraysize(kWatchTimeEvent)).data(),
-                           kv.second.InMilliseconds());
-      }
-
-      watch_time_info->clear();
-      return;
-    }
-
-    std::unique_ptr<ukm::UkmEntryBuilder> builder;
-    DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
-    for (auto power_key : watch_time_power_keys_) {
-      auto it = watch_time_info->find(power_key);
-      if (it == watch_time_info->end())
-        continue;
-      RecordWatchTime(it->first, it->second);
-
-      if (has_ukm && ShouldReportUkmWatchTime(it->first)) {
-        if (!builder)
-          builder = media_internals_->CreateUkmBuilder(url, kWatchTimeEvent);
-
-        // Strip Media.WatchTime. prefix for UKM since they're already grouped;
-        // arraysize() includes \0, so no +1 necessary for trailing period.
-        builder->AddMetric(it->first.substr(arraysize(kWatchTimeEvent)).data(),
-                           it->second.InMilliseconds());
-      }
-
-      watch_time_info->erase(it);
+        RecordWatchTimeWithFilter(url, watch_time_info,
+                                  base::flat_set<base::StringPiece>());
+        break;
+      case FinalizeType::POWER_ONLY:
+        RecordWatchTimeWithFilter(url, watch_time_info, watch_time_power_keys_);
+        break;
+      case FinalizeType::CONTROLS_ONLY:
+        RecordWatchTimeWithFilter(url, watch_time_info,
+                                  watch_time_controls_keys_);
+        break;
     }
   }
 
@@ -454,6 +455,9 @@
   // Set of only the power related watch time keys.
   const base::flat_set<base::StringPiece> watch_time_power_keys_;
 
+  // Set of only the controls related watch time keys.
+  const base::flat_set<base::StringPiece> watch_time_controls_keys_;
+
   // Mapping of WatchTime metric keys to MeanTimeBetweenRebuffers (MTBR) keys.
   const base::flat_map<base::StringPiece, base::StringPiece> mtbr_keys_;
 
@@ -466,6 +470,7 @@
     content::MediaInternals* media_internals)
     : watch_time_keys_(media::GetWatchTimeKeys()),
       watch_time_power_keys_(media::GetWatchTimePowerKeys()),
+      watch_time_controls_keys_(media::GetWatchTimeControlsKeys()),
       mtbr_keys_({{media::kWatchTimeAudioSrc,
                    media::kMeanTimeBetweenRebuffersAudioSrc},
                   {media::kWatchTimeAudioMse,
@@ -589,15 +594,28 @@
                           &player_info.underflow_count,
                           &player_info.watch_time_info,
                           FinalizeType::EVERYTHING);
-      } else if (event.params.HasKey(media::kWatchTimeFinalizePower)) {
-        bool should_finalize;
-        DCHECK(event.params.GetBoolean(media::kWatchTimeFinalizePower,
-                                       &should_finalize) &&
-               should_finalize);
-        FinalizeWatchTime(player_info.has_video, player_info.origin_url,
-                          &player_info.underflow_count,
-                          &player_info.watch_time_info,
-                          FinalizeType::POWER_ONLY);
+      } else {
+        if (event.params.HasKey(media::kWatchTimeFinalizePower)) {
+          bool should_finalize;
+          DCHECK(event.params.GetBoolean(media::kWatchTimeFinalizePower,
+                                         &should_finalize) &&
+                 should_finalize);
+          FinalizeWatchTime(player_info.has_video, player_info.origin_url,
+                            &player_info.underflow_count,
+                            &player_info.watch_time_info,
+                            FinalizeType::POWER_ONLY);
+        }
+
+        if (event.params.HasKey(media::kWatchTimeFinalizeControls)) {
+          bool should_finalize;
+          DCHECK(event.params.GetBoolean(media::kWatchTimeFinalizeControls,
+                                         &should_finalize) &&
+                 should_finalize);
+          FinalizeWatchTime(player_info.has_video, player_info.origin_url,
+                            &player_info.underflow_count,
+                            &player_info.watch_time_info,
+                            FinalizeType::CONTROLS_ONLY);
+        }
       }
       break;
     }
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc
index 1773565..3c66598 100644
--- a/content/browser/media/media_internals_unittest.cc
+++ b/content/browser/media/media_internals_unittest.cc
@@ -441,6 +441,7 @@
 
   ExpectWatchTime({media::kWatchTimeAudioAll, media::kWatchTimeAudioMse,
                    media::kWatchTimeAudioEme, media::kWatchTimeAudioAc,
+                   media::kWatchTimeAudioNativeControlsOff,
                    media::kWatchTimeAudioEmbeddedExperience},
                   kWatchTimeLate);
   ExpectMtbrTime({media::kMeanTimeBetweenRebuffersAudioMse,
@@ -448,7 +449,7 @@
                  kWatchTimeLate / 2);
 
   ASSERT_EQ(1U, test_recorder_->sources_count());
-  ExpectUkmWatchTime(0, 4, kWatchTimeLate);
+  ExpectUkmWatchTime(0, 5, kWatchTimeLate);
   EXPECT_TRUE(test_recorder_->GetSourceForUrl(kTestOrigin));
 }
 
@@ -475,6 +476,7 @@
   ExpectWatchTime(
       {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc,
        media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc,
+       media::kWatchTimeAudioVideoNativeControlsOff,
        media::kWatchTimeAudioVideoEmbeddedExperience},
       kWatchTimeLate);
   ExpectMtbrTime({media::kMeanTimeBetweenRebuffersAudioVideoSrc,
@@ -482,7 +484,7 @@
                  kWatchTimeLate / 2);
 
   ASSERT_EQ(1U, test_recorder_->sources_count());
-  ExpectUkmWatchTime(0, 4, kWatchTimeLate);
+  ExpectUkmWatchTime(0, 5, kWatchTimeLate);
   EXPECT_TRUE(test_recorder_->GetSourceForUrl(kTestOrigin));
 }
 
@@ -520,6 +522,7 @@
   std::vector<base::StringPiece> normal_keys = {
       media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc,
       media::kWatchTimeAudioVideoEme,
+      media::kWatchTimeAudioVideoNativeControlsOff,
       media::kWatchTimeAudioVideoEmbeddedExperience};
 
   for (auto key : watch_time_keys_) {
@@ -551,10 +554,114 @@
   // Spot check one of the non-AC keys; this relies on the assumption that the
   // AC metric is not last.
   const auto& metrics_vector = test_recorder_->GetEntry(1)->metrics;
-  ASSERT_EQ(4U, metrics_vector.size());
+  ASSERT_EQ(5U, metrics_vector.size());
+}
+
+TEST_F(MediaInternalsWatchTimeTest, BasicControls) {
+  constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(10);
+  constexpr base::TimeDelta kWatchTime3 = base::TimeDelta::FromSeconds(30);
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime2))
+      .WillOnce(testing::Return(kWatchTime2))
+      .WillRepeatedly(testing::Return(kWatchTime3));
+
+  Initialize(true, true, false, true);
+  wtr_->OnNativeControlsEnabled();
+  wtr_->OnPlaying();
+
+  // No log should have been generated yet since the message loop has not had
+  // any chance to pump.
+  CycleWatchTimeReporter();
+  ExpectWatchTime(std::vector<base::StringPiece>(), base::TimeDelta());
+
+  CycleWatchTimeReporter();
+
+  // Transition back to non-native controls, this should generate controls watch
+  // time as well.
+  wtr_->OnNativeControlsDisabled();
+  CycleWatchTimeReporter();
+
+  // This should finalize the power watch time on native controls.
+  ExpectWatchTime({media::kWatchTimeAudioVideoNativeControlsOn}, kWatchTime2);
+  ResetHistogramTester();
+  wtr_.reset();
+
+  std::vector<base::StringPiece> normal_keys = {
+      media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc,
+      media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc,
+      media::kWatchTimeAudioVideoEmbeddedExperience};
+
+  for (auto key : watch_time_keys_) {
+    if (key == media::kWatchTimeAudioVideoNativeControlsOff) {
+      histogram_tester_->ExpectUniqueSample(
+          key.as_string(), (kWatchTime3 - kWatchTime2).InMilliseconds(), 1);
+      continue;
+    }
+
+    auto it = std::find(normal_keys.begin(), normal_keys.end(), key);
+    if (it == normal_keys.end()) {
+      histogram_tester_->ExpectTotalCount(key.as_string(), 0);
+    } else {
+      histogram_tester_->ExpectUniqueSample(key.as_string(),
+                                            kWatchTime3.InMilliseconds(), 1);
+    }
+  }
+
+  // Each finalize creates a new source and entry. We don't check the URL here
+  // since the TestUkmService() helpers DCHECK() a unique URL per source.
+  ASSERT_EQ(2U, test_recorder_->sources_count());
+  ASSERT_EQ(2U, test_recorder_->entries_count());
+  ExpectUkmWatchTime(0, 1, kWatchTime2);
+
+  // Verify Media.WatchTime keys are properly stripped for UKM reporting.
+  EXPECT_TRUE(test_recorder_->FindMetric(test_recorder_->GetEntry(0),
+                                         "AudioVideo.NativeControlsOn"));
+
+  // Spot check one of the non-AC keys; this relies on the assumption that the
+  // AC metric is not last.
+  const auto& metrics_vector = test_recorder_->GetEntry(1)->metrics;
+  ASSERT_EQ(5U, metrics_vector.size());
   EXPECT_EQ(kWatchTime3.InMilliseconds(), metrics_vector.back()->value);
 }
 
+TEST_F(MediaInternalsWatchTimeTest, PowerControlsFinalize) {
+  constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(10);
+  constexpr base::TimeDelta kWatchTime3 = base::TimeDelta::FromSeconds(30);
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime2))
+      .WillOnce(testing::Return(kWatchTime2))
+      .WillRepeatedly(testing::Return(kWatchTime3));
+
+  Initialize(true, true, false, true);
+  wtr_->OnPlaying();
+
+  // No log should have been generated yet since the message loop has not had
+  // any chance to pump.
+  CycleWatchTimeReporter();
+  ExpectWatchTime(std::vector<base::StringPiece>(), base::TimeDelta());
+
+  CycleWatchTimeReporter();
+
+  // Transition controls and power.
+  wtr_->OnNativeControlsEnabled();
+  wtr_->OnPowerStateChangeForTesting(true);
+  CycleWatchTimeReporter();
+
+  // This should finalize the power and controls watch times.
+  ExpectWatchTime({media::kWatchTimeAudioVideoNativeControlsOff,
+                   media::kWatchTimeAudioVideoAc},
+                  kWatchTime2);
+  ResetHistogramTester();
+  wtr_.reset();
+}
+
 TEST_F(MediaInternalsWatchTimeTest, BasicHidden) {
   constexpr base::TimeDelta kWatchTimeEarly = base::TimeDelta::FromSeconds(5);
   constexpr base::TimeDelta kWatchTimeLate = base::TimeDelta::FromSeconds(10);
@@ -609,11 +716,12 @@
   ExpectWatchTime(
       {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc,
        media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc,
+       media::kWatchTimeAudioVideoNativeControlsOff,
        media::kWatchTimeAudioVideoEmbeddedExperience},
       kWatchTimeLate);
 
   ASSERT_EQ(1U, test_recorder_->sources_count());
-  ExpectUkmWatchTime(0, 4, kWatchTimeLate);
+  ExpectUkmWatchTime(0, 5, kWatchTimeLate);
   EXPECT_TRUE(test_recorder_->GetSourceForUrl(kTestOrigin));
 }
 
@@ -640,6 +748,7 @@
   ExpectWatchTime(
       {media::kWatchTimeAudioVideoAll, media::kWatchTimeAudioVideoSrc,
        media::kWatchTimeAudioVideoEme, media::kWatchTimeAudioVideoAc,
+       media::kWatchTimeAudioVideoNativeControlsOff,
        media::kWatchTimeAudioVideoEmbeddedExperience},
       kWatchTimeLate);
 }
diff --git a/content/browser/payments/payment_app_browsertest.cc b/content/browser/payments/payment_app_browsertest.cc
index c58b7af..eb99e22 100644
--- a/content/browser/payments/payment_app_browsertest.cc
+++ b/content/browser/payments/payment_app_browsertest.cc
@@ -21,8 +21,8 @@
 namespace content {
 namespace {
 
-using ::payments::mojom::PaymentAppRequest;
-using ::payments::mojom::PaymentAppRequestPtr;
+using ::payments::mojom::PaymentRequestEventData;
+using ::payments::mojom::PaymentRequestEventDataPtr;
 using ::payments::mojom::PaymentAppResponsePtr;
 using ::payments::mojom::PaymentCurrencyAmount;
 using ::payments::mojom::PaymentDetailsModifier;
@@ -108,20 +108,20 @@
       int64_t registration_id,
       const std::string& supported_method,
       const std::string& instrument_key) {
-    PaymentAppRequestPtr app_request = PaymentAppRequest::New();
+    PaymentRequestEventDataPtr event_data = PaymentRequestEventData::New();
 
-    app_request->top_level_origin = GURL("https://example.com");
+    event_data->top_level_origin = GURL("https://example.com");
 
-    app_request->payment_request_origin = GURL("https://example.com");
+    event_data->payment_request_origin = GURL("https://example.com");
 
-    app_request->payment_request_id = "payment-request-id";
+    event_data->payment_request_id = "payment-request-id";
 
-    app_request->method_data.push_back(PaymentMethodData::New());
-    app_request->method_data[0]->supported_methods = {supported_method};
+    event_data->method_data.push_back(PaymentMethodData::New());
+    event_data->method_data[0]->supported_methods = {supported_method};
 
-    app_request->total = PaymentItem::New();
-    app_request->total->amount = PaymentCurrencyAmount::New();
-    app_request->total->amount->currency = "USD";
+    event_data->total = PaymentItem::New();
+    event_data->total->amount = PaymentCurrencyAmount::New();
+    event_data->total->amount->currency = "USD";
 
     PaymentDetailsModifierPtr modifier = PaymentDetailsModifier::New();
     modifier->total = PaymentItem::New();
@@ -129,15 +129,15 @@
     modifier->total->amount->currency = "USD";
     modifier->method_data = PaymentMethodData::New();
     modifier->method_data->supported_methods = {supported_method};
-    app_request->modifiers.push_back(std::move(modifier));
+    event_data->modifiers.push_back(std::move(modifier));
 
-    app_request->instrument_key = instrument_key;
+    event_data->instrument_key = instrument_key;
 
     base::RunLoop run_loop;
     PaymentAppResponsePtr response;
     PaymentAppProvider::GetInstance()->InvokePaymentApp(
         shell()->web_contents()->GetBrowserContext(), registration_id,
-        std::move(app_request),
+        std::move(event_data),
         base::Bind(&InvokePaymentAppCallback, run_loop.QuitClosure(),
                    &response));
     run_loop.Run();
diff --git a/content/browser/payments/payment_app_content_unittest_base.cc b/content/browser/payments/payment_app_content_unittest_base.cc
index 1ce3175..37294f6 100644
--- a/content/browser/payments/payment_app_content_unittest_base.cc
+++ b/content/browser/payments/payment_app_content_unittest_base.cc
@@ -69,12 +69,12 @@
   }
 
   void OnPaymentRequestEvent(
-      payments::mojom::PaymentAppRequestPtr app_request,
+      payments::mojom::PaymentRequestEventDataPtr event_data,
       payments::mojom::PaymentAppResponseCallbackPtr response_callback,
       mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback
           callback) override {
     EmbeddedWorkerTestHelper::OnPaymentRequestEvent(
-        std::move(app_request), std::move(response_callback),
+        std::move(event_data), std::move(response_callback),
         std::move(callback));
   }
 
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc
index 73f3cba..43931929 100644
--- a/content/browser/payments/payment_app_provider_impl.cc
+++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -79,7 +79,7 @@
 }
 
 void DispatchPaymentRequestEvent(
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     const PaymentAppProvider::InvokePaymentAppCallback& callback,
     scoped_refptr<ServiceWorkerVersion> active_version) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -96,13 +96,13 @@
       ResponseCallback::Create(payment_request_id, active_version, callback);
   DCHECK(response_callback_ptr);
   active_version->event_dispatcher()->DispatchPaymentRequestEvent(
-      payment_request_id, std::move(app_request),
+      payment_request_id, std::move(event_data),
       std::move(response_callback_ptr),
       active_version->CreateSimpleEventCallback(event_finish_id));
 }
 
 void DidFindRegistrationOnIO(
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     const PaymentAppProvider::InvokePaymentAppCallback& callback,
     ServiceWorkerStatusCode service_worker_status,
     scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
@@ -117,7 +117,7 @@
   active_version->RunAfterStartWorker(
       ServiceWorkerMetrics::EventType::PAYMENT_REQUEST,
       base::Bind(&DispatchPaymentRequestEvent,
-                 base::Passed(std::move(app_request)), callback,
+                 base::Passed(std::move(event_data)), callback,
                  make_scoped_refptr(active_version)),
       base::Bind(&DispatchPaymentRequestEventError));
 }
@@ -125,13 +125,13 @@
 void FindRegistrationOnIO(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
     int64_t registration_id,
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     const PaymentAppProvider::InvokePaymentAppCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   service_worker_context->FindReadyRegistrationForIdOnly(
       registration_id,
-      base::Bind(&DidFindRegistrationOnIO, base::Passed(std::move(app_request)),
+      base::Bind(&DidFindRegistrationOnIO, base::Passed(std::move(event_data)),
                  callback));
 }
 
@@ -167,7 +167,7 @@
 void PaymentAppProviderImpl::InvokePaymentApp(
     BrowserContext* browser_context,
     int64_t registration_id,
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     const InvokePaymentAppCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -179,7 +179,7 @@
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&FindRegistrationOnIO, std::move(service_worker_context),
-                 registration_id, base::Passed(std::move(app_request)),
+                 registration_id, base::Passed(std::move(event_data)),
                  callback));
 }
 
diff --git a/content/browser/payments/payment_app_provider_impl.h b/content/browser/payments/payment_app_provider_impl.h
index 6daeee99..0bc5443 100644
--- a/content/browser/payments/payment_app_provider_impl.h
+++ b/content/browser/payments/payment_app_provider_impl.h
@@ -22,7 +22,7 @@
                          GetAllPaymentAppsCallback callback) override;
   void InvokePaymentApp(BrowserContext* browser_context,
                         int64_t registration_id,
-                        payments::mojom::PaymentAppRequestPtr app_request,
+                        payments::mojom::PaymentRequestEventDataPtr event_data,
                         const InvokePaymentAppCallback& callback) override;
 
  private:
diff --git a/content/browser/payments/payment_app_provider_impl_unittest.cc b/content/browser/payments/payment_app_provider_impl_unittest.cc
index fbc9fcd0..08f832c 100644
--- a/content/browser/payments/payment_app_provider_impl_unittest.cc
+++ b/content/browser/payments/payment_app_provider_impl_unittest.cc
@@ -65,10 +65,10 @@
   }
 
   void InvokePaymentApp(int64_t registration_id,
-                        payments::mojom::PaymentAppRequestPtr app_request,
+                        payments::mojom::PaymentRequestEventDataPtr event_data,
                         PaymentAppProvider::InvokePaymentAppCallback callback) {
     PaymentAppProviderImpl::GetInstance()->InvokePaymentApp(
-        browser_context(), registration_id, std::move(app_request), callback);
+        browser_context(), registration_id, std::move(event_data), callback);
     base::RunLoop().RunUntilIdle();
   }
 
@@ -97,15 +97,15 @@
   GetAllPaymentApps(base::Bind(&GetAllPaymentAppsCallback, &apps));
   ASSERT_EQ(2U, apps.size());
 
-  payments::mojom::PaymentAppRequestPtr app_request =
-      payments::mojom::PaymentAppRequest::New();
-  app_request->method_data.push_back(payments::mojom::PaymentMethodData::New());
-  app_request->total = payments::mojom::PaymentItem::New();
-  app_request->total->amount = payments::mojom::PaymentCurrencyAmount::New();
+  payments::mojom::PaymentRequestEventDataPtr event_data =
+      payments::mojom::PaymentRequestEventData::New();
+  event_data->method_data.push_back(payments::mojom::PaymentMethodData::New());
+  event_data->total = payments::mojom::PaymentItem::New();
+  event_data->total->amount = payments::mojom::PaymentCurrencyAmount::New();
 
   bool called = false;
   InvokePaymentApp(apps[GURL("https://hellopay.com/")][0]->registration_id,
-                   std::move(app_request),
+                   std::move(event_data),
                    base::Bind(&InvokePaymentAppCallback, &called));
   ASSERT_TRUE(called);
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e26d27268..9fa9d2b3 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2157,7 +2157,6 @@
     switches::kEnableTouchDragDrop,
     switches::kEnableUseZoomForDSF,
     switches::kEnableViewport,
-    switches::kEnableVp9InMp4,
     switches::kEnableVtune,
     switches::kEnableWebFontsInterventionTrigger,
     switches::kEnableWebFontsInterventionV2,
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 2cad1fb9..9a97fd2 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -119,4 +119,8 @@
   return nullptr;
 }
 
+bool RenderWidgetHostDelegate::IsShowingContextMenuOnPage() const {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 29b3115..86bcb17 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -272,6 +272,9 @@
   // Reset the auto-size value, to indicate that auto-size is no longer active.
   virtual void ResetAutoResizeSize() {}
 
+  // Returns true if there is context menu shown on page.
+  virtual bool IsShowingContextMenuOnPage() const;
+
  protected:
   virtual ~RenderWidgetHostDelegate() {}
 };
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index 9a6befb..b940e3b 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -35,7 +35,6 @@
     : is_fullscreen_(false),
       popup_type_(blink::kWebPopupTypeNone),
       mouse_locked_(false),
-      showing_context_menu_(false),
       current_device_scale_factor_(0),
       current_display_rotation_(display::Display::ROTATE_0),
       text_input_manager_(nullptr),
@@ -163,15 +162,6 @@
   callback.Run(gfx::Rect(), false);
 }
 
-bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
-  return showing_context_menu_;
-}
-
-void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) {
-  DCHECK_NE(showing_context_menu_, showing);
-  showing_context_menu_ = showing;
-}
-
 base::string16 RenderWidgetHostViewBase::GetSelectedText() {
   if (!GetTextInputManager())
     return base::string16();
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 59aa4a1..ab9c08b 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -103,8 +103,6 @@
   ui::TextInputClient* GetTextInputClient() override;
   void WasUnOccluded() override {}
   void WasOccluded() override {}
-  bool IsShowingContextMenu() const override;
-  void SetShowingContextMenu(bool showing_menu) override;
   void SetIsInVR(bool is_in_vr) override;
   base::string16 GetSelectedText() override;
   bool IsMouseLocked() override;
@@ -410,6 +408,11 @@
   // main frame.
   virtual void OnDidNavigateMainFrameToNewPage();
 
+  // Called by WebContentsImpl to notify the view about a change in visibility
+  // of context menu. The view can then perform platform specific tasks and
+  // changes.
+  virtual void SetShowingContextMenu(bool showing) {}
+
   // Add and remove observers for lifetime event notifications. The order in
   // which notifications are sent to observers is undefined. Clients must be
   // sure to remove the observer before they go away.
@@ -465,9 +468,6 @@
   // locked.
   bool mouse_locked_;
 
-  // Whether we are showing a context menu.
-  bool showing_context_menu_;
-
   // The scale factor of the display the renderer is currently on.
   float current_device_scale_factor_;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index f0c1e1c..995d2e5 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -577,7 +577,7 @@
     // Don't forward the mouse leave message which is received when the context
     // menu is displayed by the page. This confuses the page and causes state
     // changes.
-    if (host_view_->IsShowingContextMenu())
+    if (host_->delegate() && host_->delegate()->IsShowingContextMenuOnPage())
       return false;
 #endif
     return true;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 5ea52b3..17803a94 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -269,7 +269,6 @@
   void WasUnOccluded() override;
   void WasOccluded() override;
   gfx::Rect GetViewBounds() const override;
-  void SetShowingContextMenu(bool showing) override;
   void SetActive(bool active) override;
   void ShowDefinitionForSelection() override;
   bool SupportsSpeech() const override;
@@ -464,6 +463,8 @@
   // Exposed for testing.
   cc::SurfaceId SurfaceIdForTesting() const override;
 
+  void SetShowingContextMenu(bool showing) override;
+
   // Helper method to obtain ui::TextInputType for the active widget from the
   // TextInputManager.
   ui::TextInputType GetTextInputType();
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 8febafce..b71d8d6 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1134,8 +1134,6 @@
 //
 
 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
-  RenderWidgetHostViewBase::SetShowingContextMenu(showing);
-
   // Create a fake mouse event to inform the render widget that the mouse
   // left or entered.
   NSWindow* window = [cocoa_view_ window];
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index d1bb82f..13945c58 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -255,12 +255,12 @@
 
   void DispatchPaymentRequestEvent(
       int payment_request_id,
-      payments::mojom::PaymentAppRequestPtr app_request,
+      payments::mojom::PaymentRequestEventDataPtr event_data,
       payments::mojom::PaymentAppResponseCallbackPtr response_callback,
       DispatchPaymentRequestEventCallback callback) override {
     if (!helper_)
       return;
-    helper_->OnPaymentRequestEventStub(std::move(app_request),
+    helper_->OnPaymentRequestEventStub(std::move(event_data),
                                        std::move(response_callback),
                                        std::move(callback));
   }
@@ -520,7 +520,7 @@
 }
 
 void EmbeddedWorkerTestHelper::OnPaymentRequestEvent(
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     payments::mojom::PaymentAppResponseCallbackPtr response_callback,
     mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback
         callback) {
@@ -783,14 +783,14 @@
 }
 
 void EmbeddedWorkerTestHelper::OnPaymentRequestEventStub(
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr event_data,
     payments::mojom::PaymentAppResponseCallbackPtr response_callback,
     mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback
         callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::Bind(&EmbeddedWorkerTestHelper::OnPaymentRequestEvent, AsWeakPtr(),
-                 base::Passed(&app_request), base::Passed(&response_callback),
+                 base::Passed(&event_data), base::Passed(&response_callback),
                  base::Passed(&callback)));
 }
 
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index c19b0773..bb4f98b 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -231,7 +231,7 @@
       const PushEventPayload& payload,
       mojom::ServiceWorkerEventDispatcher::DispatchPushEventCallback callback);
   virtual void OnPaymentRequestEvent(
-      payments::mojom::PaymentAppRequestPtr data,
+      payments::mojom::PaymentRequestEventDataPtr data,
       payments::mojom::PaymentAppResponseCallbackPtr response_callback,
       mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback
           callback);
@@ -316,7 +316,7 @@
       const PushEventPayload& payload,
       mojom::ServiceWorkerEventDispatcher::DispatchPushEventCallback callback);
   void OnPaymentRequestEventStub(
-      payments::mojom::PaymentAppRequestPtr data,
+      payments::mojom::PaymentRequestEventDataPtr data,
       payments::mojom::PaymentAppResponseCallbackPtr response_callback,
       mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback
           callback);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 629c1f48..99cfc118 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -522,6 +522,7 @@
 #endif  // !defined(OS_ANDROID)
       mouse_lock_widget_(nullptr),
       is_overlay_content_(false),
+      showing_context_menu_(false),
       loading_weak_factory_(this),
       weak_factory_(this) {
   frame_tree_.SetFrameRemoveListener(
@@ -4394,7 +4395,7 @@
                                       const ContextMenuParams& params) {
   // If a renderer fires off a second command to show a context menu before the
   // first context menu is closed, just ignore it. https://crbug.com/707534
-  if (GetRenderWidgetHostView()->IsShowingContextMenu())
+  if (showing_context_menu_)
     return;
 
   ContextMenuParams context_menu_params(params);
@@ -4508,6 +4509,21 @@
   return frame && frame->has_focused_editable_element();
 }
 
+bool WebContentsImpl::IsShowingContextMenu() const {
+  return showing_context_menu_;
+}
+
+void WebContentsImpl::SetShowingContextMenu(bool showing) {
+  DCHECK_NE(showing_context_menu_, showing);
+  showing_context_menu_ = showing;
+
+  if (auto* view = GetRenderWidgetHostView()) {
+    // Notify the main frame's RWHV to run the platform-specific code, if any.
+    static_cast<RenderWidgetHostViewBase*>(view)->SetShowingContextMenu(
+        showing);
+  }
+}
+
 void WebContentsImpl::ClearFocusedElement() {
   if (auto* frame = GetFocusedFrame())
     frame->ClearFocusedElement();
@@ -5660,6 +5676,10 @@
                          " releasing your website to the public."));
 }
 
+bool WebContentsImpl::IsShowingContextMenuOnPage() const {
+  return showing_context_menu_;
+}
+
 void WebContentsImpl::NotifyPreferencesChanged() {
   std::set<RenderViewHost*> render_view_host_set;
   for (FrameTreeNode* node : frame_tree_.Nodes()) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 2ebfb67..0bb0222 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -456,6 +456,8 @@
   void SetIsOverlayContent(bool is_overlay_content) override;
   bool IsFocusedElementEditable() override;
   void ClearFocusedElement() override;
+  bool IsShowingContextMenu() const override;
+  void SetShowingContextMenu(bool showing) override;
 
 #if defined(OS_ANDROID)
   base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents() override;
@@ -720,6 +722,7 @@
                              ukm::SourceId ukm_source_id) override;
   void FocusedNodeTouched(bool editable) override;
   void DidReceiveCompositorFrame() override;
+  bool IsShowingContextMenuOnPage() const override;
 
   // RenderFrameHostManager::Delegate ------------------------------------------
 
@@ -1607,6 +1610,8 @@
   // Whether this WebContents is for content overlay.
   bool is_overlay_content_;
 
+  bool showing_context_menu_;
+
   int currently_playing_video_count_ = 0;
 
   bool has_persistent_video_ = false;
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 1f5cc79..e2c64faa 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -355,6 +355,9 @@
   if (base::FeatureList::IsEnabled(features::kLoadingWithMojo))
     WebRuntimeFeatures::EnableLoadingWithMojo(true);
 
+  WebRuntimeFeatures::EnableMediaCastOverlayButton(
+      base::FeatureList::IsEnabled(media::kMediaCastOverlayButton));
+
   if (!base::FeatureList::IsEnabled(features::kBlockCredentialedSubresources)) {
     WebRuntimeFeatures::EnableFeatureFromString("BlockCredentialedSubresources",
                                                 false);
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker_event_dispatcher.mojom
index 1c926e8..0ff3dc0 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker_event_dispatcher.mojom
@@ -132,7 +132,7 @@
       => (blink.mojom.ServiceWorkerEventStatus status,
           mojo.common.mojom.Time dispatch_event_time);
   DispatchPaymentRequestEvent(int32 payment_request_id,
-                              payments.mojom.PaymentAppRequest app_request,
+                              payments.mojom.PaymentRequestEventData request_data,
                               payments.mojom.PaymentAppResponseCallback response_callback)
       => (blink.mojom.ServiceWorkerEventStatus status,
           mojo.common.mojom.Time dispatch_event_time);
diff --git a/content/public/browser/payment_app_provider.h b/content/public/browser/payment_app_provider.h
index 1d50554..8f44e4e9 100644
--- a/content/public/browser/payment_app_provider.h
+++ b/content/public/browser/payment_app_provider.h
@@ -45,7 +45,7 @@
   virtual void InvokePaymentApp(
       BrowserContext* browser_context,
       int64_t registration_id,
-      payments::mojom::PaymentAppRequestPtr app_request,
+      payments::mojom::PaymentRequestEventDataPtr event_data,
       const InvokePaymentAppCallback& callback) = 0;
 
  protected:
diff --git a/content/public/browser/render_widget_host_view.h b/content/public/browser/render_widget_host_view.h
index e295ef25..55281d3 100644
--- a/content/public/browser/render_widget_host_view.h
+++ b/content/public/browser/render_widget_host_view.h
@@ -122,12 +122,6 @@
   // Retrieve the bounds of the View, in screen coordinates.
   virtual gfx::Rect GetViewBounds() const = 0;
 
-  // Returns true if the View's context menu is showing.
-  virtual bool IsShowingContextMenu() const = 0;
-
-  // Tells the View whether the context menu is showing.
-  virtual void SetShowingContextMenu(bool showing) = 0;
-
   // Returns the currently selected text.
   virtual base::string16 GetSelectedText() = 0;
 
diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h
index fbbe19ae..f22ab21 100644
--- a/content/public/browser/ssl_host_state_delegate.h
+++ b/content/public/browser/ssl_host_state_delegate.h
@@ -9,7 +9,6 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
 #include "content/common/content_export.h"
 #include "net/cert/x509_certificate.h"
 
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 352bda3..3e1ed690 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -782,6 +782,12 @@
   // Returns true if the current focused element is editable.
   virtual bool IsFocusedElementEditable() = 0;
 
+  // Returns true if a context menu is showing on the page.
+  virtual bool IsShowingContextMenu() const = 0;
+
+  // Tells the WebContents whether the context menu is showing.
+  virtual void SetShowingContextMenu(bool showing) = 0;
+
 #if defined(OS_ANDROID)
   CONTENT_EXPORT static WebContents* FromJavaWebContents(
       const base::android::JavaRef<jobject>& jweb_contents_android);
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 5d510fe..01fc07d 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -194,6 +194,11 @@
   return ::media::IsSupportedVideoConfig(config);
 }
 
+bool ContentRendererClient::IsSupportedBitstreamAudioCodec(
+    media::AudioCodec codec) {
+  return false;
+}
+
 std::unique_ptr<MediaStreamRendererFactory>
 ContentRendererClient::CreateMediaStreamRendererFactory() {
   return nullptr;
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 7a3ecf5..3cfb080 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -275,6 +275,9 @@
   // Allows embedder to describe customized video capabilities.
   virtual bool IsSupportedVideoConfig(const media::VideoConfig& config);
 
+  // Return true if the bitstream format |codec| is supported by the audio sink.
+  virtual bool IsSupportedBitstreamAudioCodec(media::AudioCodec codec);
+
   // Returns true if we should report a detailed message (including a stack
   // trace) for console [logs|errors|exceptions]. |source| is the WebKit-
   // reported source for the error; this can point to a page or a script,
diff --git a/content/renderer/media/render_media_client.cc b/content/renderer/media/render_media_client.cc
index c3d146761..6447901c 100644
--- a/content/renderer/media/render_media_client.cc
+++ b/content/renderer/media/render_media_client.cc
@@ -44,4 +44,9 @@
   return GetContentClient()->renderer()->IsSupportedVideoConfig(config);
 }
 
+bool RenderMediaClient::IsSupportedBitstreamAudioCodec(
+    media::AudioCodec codec) {
+  return GetContentClient()->renderer()->IsSupportedBitstreamAudioCodec(codec);
+}
+
 }  // namespace content
diff --git a/content/renderer/media/render_media_client.h b/content/renderer/media/render_media_client.h
index c439e6e..2fd7c65 100644
--- a/content/renderer/media/render_media_client.h
+++ b/content/renderer/media/render_media_client.h
@@ -25,6 +25,7 @@
   bool IsKeySystemsUpdateNeeded() final;
   bool IsSupportedAudioConfig(const media::AudioConfig& config) final;
   bool IsSupportedVideoConfig(const media::VideoConfig& config) final;
+  bool IsSupportedBitstreamAudioCodec(media::AudioCodec codec) final;
 
  private:
   RenderMediaClient();
diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc
index 96d56a5..3f89c88 100644
--- a/content/renderer/media/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/webmediaplayer_ms_unittest.cc
@@ -464,6 +464,7 @@
   blink::WebMediaPlayer::TrackId GetSelectedVideoTrackId() override {
     return blink::WebMediaPlayer::TrackId();
   }
+  bool HasNativeControls() override { return false; }
 
   // Implementation of cc::VideoFrameProvider::Client
   void StopUsingProvider() override;
diff --git a/content/renderer/mojo_main_runner.cc b/content/renderer/mojo_main_runner.cc
index be12b920..0fb5ebe 100644
--- a/content/renderer/mojo_main_runner.cc
+++ b/content/renderer/mojo_main_runner.cc
@@ -8,7 +8,7 @@
 #include "gin/modules/module_registry.h"
 #include "gin/per_context_data.h"
 #include "gin/public/context_holder.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebScriptSource.h"
 
 using v8::Context;
@@ -20,10 +20,9 @@
 
 namespace content {
 
-MojoMainRunner::MojoMainRunner(blink::WebFrame* frame,
+MojoMainRunner::MojoMainRunner(blink::WebLocalFrame* frame,
                                gin::ContextHolder* context_holder)
-    : frame_(frame),
-      context_holder_(context_holder) {
+    : frame_(frame), context_holder_(context_holder) {
   DCHECK(frame_);
   v8::Isolate::Scope isolate_scope(context_holder->isolate());
   HandleScope handle_scope(context_holder->isolate());
diff --git a/content/renderer/mojo_main_runner.h b/content/renderer/mojo_main_runner.h
index c138c42..f8087bf 100644
--- a/content/renderer/mojo_main_runner.h
+++ b/content/renderer/mojo_main_runner.h
@@ -9,7 +9,7 @@
 #include "gin/runner.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace content {
@@ -18,7 +18,8 @@
 class MojoMainRunner : public gin::Runner {
  public:
   // Does not take ownership of ContextHolder.
-  MojoMainRunner(blink::WebFrame* frame, gin::ContextHolder* context_holder);
+  MojoMainRunner(blink::WebLocalFrame* frame,
+                 gin::ContextHolder* context_holder);
   ~MojoMainRunner() override;
 
   // Runner overrides:
@@ -32,7 +33,7 @@
 
  private:
   // Frame to execute script in.
-  blink::WebFrame* frame_;
+  blink::WebLocalFrame* frame_;
 
   // Created by blink bindings to V8.
   gin::ContextHolder* context_holder_;
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index bf68c1c..a342281 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -65,8 +65,8 @@
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/platform/modules/background_fetch/WebBackgroundFetchSettledFetch.h"
 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationData.h"
-#include "third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h"
 #include "third_party/WebKit/public/platform/modules/payments/WebPaymentAppResponse.h"
+#include "third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerClientQueryOptions.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
@@ -1213,7 +1213,7 @@
 
 void ServiceWorkerContextClient::DispatchPaymentRequestEvent(
     int payment_request_id,
-    payments::mojom::PaymentAppRequestPtr app_request,
+    payments::mojom::PaymentRequestEventDataPtr eventData,
     payments::mojom::PaymentAppResponseCallbackPtr response_callback,
     DispatchPaymentRequestEventCallback callback) {
   TRACE_EVENT0("ServiceWorker",
@@ -1223,9 +1223,9 @@
   context_->payment_request_event_callbacks.insert(
       std::make_pair(payment_request_id, std::move(callback)));
 
-  blink::WebPaymentAppRequest webAppRequest =
-      mojo::ConvertTo<blink::WebPaymentAppRequest>(std::move(app_request));
-  proxy_->DispatchPaymentRequestEvent(payment_request_id, webAppRequest);
+  blink::WebPaymentRequestEventData webEventData =
+      mojo::ConvertTo<blink::WebPaymentRequestEventData>(std::move(eventData));
+  proxy_->DispatchPaymentRequestEvent(payment_request_id, webEventData);
 }
 
 void ServiceWorkerContextClient::Send(IPC::Message* message) {
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index c062314e..12aacbf 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -285,7 +285,7 @@
       DispatchSyncEventCallback callback) override;
   void DispatchPaymentRequestEvent(
       int payment_request_id,
-      payments::mojom::PaymentAppRequestPtr app_request,
+      payments::mojom::PaymentRequestEventDataPtr event_data,
       payments::mojom::PaymentAppResponseCallbackPtr response_callback,
       DispatchPaymentRequestEventCallback callback) override;
   void Ping(PingCallback callback) override;
diff --git a/content/renderer/service_worker/service_worker_type_converters.cc b/content/renderer/service_worker/service_worker_type_converters.cc
index 9899d95..50bbfbe7 100644
--- a/content/renderer/service_worker/service_worker_type_converters.cc
+++ b/content/renderer/service_worker/service_worker_type_converters.cc
@@ -29,11 +29,11 @@
   return status_code;
 }
 
-blink::WebPaymentAppRequest
-TypeConverter<blink::WebPaymentAppRequest,
-              payments::mojom::PaymentAppRequestPtr>::
-    Convert(const payments::mojom::PaymentAppRequestPtr& input) {
-  blink::WebPaymentAppRequest output;
+blink::WebPaymentRequestEventData
+TypeConverter<blink::WebPaymentRequestEventData,
+              payments::mojom::PaymentRequestEventDataPtr>::
+    Convert(const payments::mojom::PaymentRequestEventDataPtr& input) {
+  blink::WebPaymentRequestEventData output;
 
   output.top_level_origin =
       blink::WebString::FromUTF8(input->top_level_origin.spec());
diff --git a/content/renderer/service_worker/service_worker_type_converters.h b/content/renderer/service_worker/service_worker_type_converters.h
index 28b5e68a..61ad863 100644
--- a/content/renderer/service_worker/service_worker_type_converters.h
+++ b/content/renderer/service_worker/service_worker_type_converters.h
@@ -8,7 +8,7 @@
 #include "components/payments/mojom/payment_app.mojom.h"
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
 #include "content/common/service_worker/service_worker_status_code.h"
-#include "third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h"
+#include "third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h"
 #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h"
 
@@ -22,10 +22,10 @@
 };
 
 template <>
-struct TypeConverter<blink::WebPaymentAppRequest,
-                     payments::mojom::PaymentAppRequestPtr> {
-  static blink::WebPaymentAppRequest Convert(
-      const payments::mojom::PaymentAppRequestPtr& input);
+struct TypeConverter<blink::WebPaymentRequestEventData,
+                     payments::mojom::PaymentRequestEventDataPtr> {
+  static blink::WebPaymentRequestEventData Convert(
+      const payments::mojom::PaymentRequestEventDataPtr& input);
 };
 
 template <>
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 91c408b..416872b6d 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -2040,7 +2040,13 @@
   WorkItemLoadingScript(const std::string& script) : script_(script) {}
 
   bool Run(WebTestDelegate*, WebView* web_view) override {
-    web_view->MainFrame()->ExecuteScript(
+    blink::WebFrame* main_frame = web_view->MainFrame();
+    if (!main_frame->IsWebLocalFrame()) {
+      CHECK(false) << "This function cannot be called if the main frame is not "
+                      "a local frame.";
+      return false;
+    }
+    main_frame->ToWebLocalFrame()->ExecuteScript(
         WebScriptSource(WebString::FromUTF8(script_)));
     return true;  // FIXME: Did it really start a navigation?
   }
@@ -2058,7 +2064,13 @@
   WorkItemNonLoadingScript(const std::string& script) : script_(script) {}
 
   bool Run(WebTestDelegate*, WebView* web_view) override {
-    web_view->MainFrame()->ExecuteScript(
+    blink::WebFrame* main_frame = web_view->MainFrame();
+    if (!main_frame->IsWebLocalFrame()) {
+      CHECK(false) << "This function cannot be called if the main frame is not "
+                      "a local frame.";
+      return false;
+    }
+    main_frame->ToWebLocalFrame()->ExecuteScript(
         WebScriptSource(WebString::FromUTF8(script_)));
     return false;
   }
diff --git a/extensions/browser/updater/update_install_shim.cc b/extensions/browser/updater/update_install_shim.cc
index 04621b0..ac3b65c 100644
--- a/extensions/browser/updater/update_install_shim.cc
+++ b/extensions/browser/updater/update_install_shim.cc
@@ -33,8 +33,9 @@
   VLOG(1) << "OnUpdateError (" << extension_id_ << ") " << error;
 }
 
-Result UpdateInstallShim::Install(const base::DictionaryValue& manifest,
-                                  const base::FilePath& unpack_path) {
+Result UpdateInstallShim::Install(
+    std::unique_ptr<base::DictionaryValue> manifest,
+    const base::FilePath& unpack_path) {
   base::ScopedTempDir temp_dir;
   if (!temp_dir.CreateUniqueTempDir())
     return Result(InstallError::GENERIC_ERROR);
diff --git a/extensions/browser/updater/update_install_shim.h b/extensions/browser/updater/update_install_shim.h
index e6e9d93..82d2d3a 100644
--- a/extensions/browser/updater/update_install_shim.h
+++ b/extensions/browser/updater/update_install_shim.h
@@ -5,6 +5,7 @@
 #ifndef EXTENSIONS_BROWSER_UPDATER_UPDATE_INSTALL_SHIM_H_
 #define EXTENSIONS_BROWSER_UPDATER_UPDATE_INSTALL_SHIM_H_
 
+#include <memory>
 #include <string>
 
 #include "base/callback.h"
@@ -44,7 +45,7 @@
 
   // This is called when a new version of an extension is unpacked at
   // |unpack_path| and is ready for install.
-  CrxInstaller::Result Install(const base::DictionaryValue& manifest,
+  CrxInstaller::Result Install(std::unique_ptr<base::DictionaryValue> manifest,
                                const base::FilePath& unpack_path) override;
 
   // This is called by the generic differential update code in the
diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc
index d75329c..4db5545 100644
--- a/extensions/browser/updater/update_service_unittest.cc
+++ b/extensions/browser/updater/update_service_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <stddef.h>
 
+#include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/files/file_util.h"
@@ -268,7 +270,7 @@
       extension1->manifest()->value()->DeepCopy());
   new_manifest->SetString("version", "2.0");
 
-  installer->Install(*new_manifest, new_version_dir.GetPath());
+  installer->Install(std::move(new_manifest), new_version_dir.GetPath());
 
   scoped_refptr<content::MessageLoopRunner> loop_runner =
       new content::MessageLoopRunner();
diff --git a/gpu/command_buffer/common/discardable_handle.cc b/gpu/command_buffer/common/discardable_handle.cc
index d7fa9cd..31f7f51 100644
--- a/gpu/command_buffer/common/discardable_handle.cc
+++ b/gpu/command_buffer/common/discardable_handle.cc
@@ -30,6 +30,18 @@
 DiscardableHandleBase& DiscardableHandleBase::operator=(
     DiscardableHandleBase&& other) = default;
 
+bool DiscardableHandleBase::ValidateParameters(const Buffer* buffer,
+                                               uint32_t byte_offset) {
+  if (!buffer)
+    return false;
+  if (byte_offset % sizeof(base::subtle::Atomic32))
+    return false;
+  if (!buffer->GetDataAddress(byte_offset, sizeof(base::subtle::Atomic32)))
+    return false;
+
+  return true;
+}
+
 bool DiscardableHandleBase::IsLockedForTesting() const {
   return kHandleLockedStart <= base::subtle::NoBarrier_Load(AsAtomic());
 }
diff --git a/gpu/command_buffer/common/discardable_handle.h b/gpu/command_buffer/common/discardable_handle.h
index 7d4c5286..b3e5127c 100644
--- a/gpu/command_buffer/common/discardable_handle.h
+++ b/gpu/command_buffer/common/discardable_handle.h
@@ -36,6 +36,9 @@
   int32_t shm_id() const { return shm_id_; }
   uint32_t byte_offset() const { return byte_offset_; }
 
+  // Ensures this is a valid allocation for use with a DiscardableHandleBase.
+  static bool ValidateParameters(const Buffer* buffer, uint32_t byte_offset);
+
   // Test only functions.
   bool IsLockedForTesting() const;
   bool IsDeletedForTesting() const;
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index 637c6b7..e803262 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -86,8 +86,9 @@
 
   bool CanRenderTo(const FeatureInfo*) const override { return true; }
 
-  void DetachFromFramebuffer(Framebuffer* framebuffer) const override {
-    // Nothing to do for renderbuffers.
+  void DetachFromFramebuffer(Framebuffer* framebuffer,
+                             GLenum attachment) const override {
+    renderbuffer_->RemoveFramebufferAttachmentPoint(framebuffer, attachment);
   }
 
   bool IsLayerValid() const override { return true; }
@@ -238,7 +239,8 @@
     return texture_ref_->texture()->CanRenderTo(feature_info, level_);
   }
 
-  void DetachFromFramebuffer(Framebuffer* framebuffer) const override {
+  void DetachFromFramebuffer(Framebuffer* framebuffer,
+                             GLenum attachment) const override {
     texture_ref_->texture()->DetachFromFramebuffer();
   }
 
@@ -338,9 +340,10 @@
 void Framebuffer::MarkAsDeleted() {
   deleted_ = true;
   while (!attachments_.empty()) {
-    Attachment* attachment = attachments_.begin()->second.get();
-    attachment->DetachFromFramebuffer(this);
-    attachments_.erase(attachments_.begin());
+    auto entry = attachments_.begin();
+    Attachment* attachment = entry->second.get();
+    attachment->DetachFromFramebuffer(this, entry->first);
+    attachments_.erase(entry);
   }
 }
 
@@ -402,6 +405,10 @@
     manager_->StopTracking(this);
     manager_ = NULL;
   }
+
+  for (auto& attachment : attachments_) {
+    attachment.second->DetachFromFramebuffer(this, attachment.first);
+  }
 }
 
 bool Framebuffer::HasUnclearedAttachment(
@@ -983,14 +990,15 @@
   DCHECK(attachment != GL_DEPTH_STENCIL_ATTACHMENT);
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer(this);
+    a->DetachFromFramebuffer(this, attachment);
   if (renderbuffer) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new RenderbufferAttachment(renderbuffer));
+    renderbuffer->AddFramebufferAttachmentPoint(this, attachment);
   } else {
     attachments_.erase(attachment);
   }
-  framebuffer_complete_state_count_id_ = 0;
+  UnmarkAsComplete();
 }
 
 void Framebuffer::AttachTexture(
@@ -999,7 +1007,7 @@
   DCHECK(attachment != GL_DEPTH_STENCIL_ATTACHMENT);
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer(this);
+    a->DetachFromFramebuffer(this, attachment);
   if (texture_ref) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new TextureAttachment(texture_ref, target, level, samples, 0));
@@ -1007,7 +1015,7 @@
   } else {
     attachments_.erase(attachment);
   }
-  framebuffer_complete_state_count_id_ = 0;
+  UnmarkAsComplete();
 }
 
 void Framebuffer::AttachTextureLayer(
@@ -1016,7 +1024,7 @@
   DCHECK(attachment != GL_DEPTH_STENCIL_ATTACHMENT);
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer(this);
+    a->DetachFromFramebuffer(this, attachment);
   if (texture_ref) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new TextureAttachment(texture_ref, target, level, 0, layer));
@@ -1024,7 +1032,7 @@
   } else {
     attachments_.erase(attachment);
   }
-  framebuffer_complete_state_count_id_ = 0;
+  UnmarkAsComplete();
 }
 
 const Framebuffer::Attachment*
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index e4433cc..182032b9 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -60,7 +60,8 @@
     virtual bool IsLayerValid() const = 0;
 
     virtual bool CanRenderTo(const FeatureInfo* feature_info) const = 0;
-    virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const = 0;
+    virtual void DetachFromFramebuffer(Framebuffer* framebuffer,
+                                       GLenum attachment) const = 0;
     virtual bool ValidForAttachmentType(GLenum attachment_type,
                                         uint32_t max_color_attachments) = 0;
     virtual size_t GetSignatureSize(TextureManager* texture_manager) const = 0;
@@ -225,6 +226,8 @@
     return draw_buffer_bound_mask_;
   }
 
+  void UnmarkAsComplete() { framebuffer_complete_state_count_id_ = 0; }
+
  private:
   friend class FramebufferManager;
   friend class base::RefCounted<Framebuffer>;
diff --git a/gpu/command_buffer/service/framebuffer_manager_unittest.cc b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
index 513a219..b415ab5 100644
--- a/gpu/command_buffer/service/framebuffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
@@ -251,14 +251,14 @@
   EXPECT_TRUE(framebuffer_->IsCleared());
 
   // Try a format that's not good for COLOR_ATTACHMENT0.
-  renderbuffer_manager_->SetInfo(
-      renderbuffer1, kSamples1, kBadFormat1, kWidth1, kHeight1);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
+                                              kBadFormat1, kWidth1, kHeight1);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
             framebuffer_->IsPossiblyComplete(feature_info_.get()));
 
   // Try a good format.
-  renderbuffer_manager_->SetInfo(
-      renderbuffer1, kSamples1, kFormat1, kWidth1, kHeight1);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
+                                              kFormat1, kWidth1, kHeight1);
   EXPECT_EQ(static_cast<GLenum>(kFormat1),
             framebuffer_->GetReadBufferInternalFormat());
   EXPECT_FALSE(framebuffer_->HasDepthAttachment());
@@ -290,8 +290,8 @@
       status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT);
   EXPECT_FALSE(framebuffer_->IsCleared());
 
-  renderbuffer_manager_->SetInfo(
-      renderbuffer2, kSamples2, kFormat2, kWidth2, kHeight2);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer2, kSamples2,
+                                              kFormat2, kWidth2, kHeight2);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
             framebuffer_->IsPossiblyComplete(feature_info_.get()));
   EXPECT_FALSE(framebuffer_->IsCleared());
@@ -312,8 +312,8 @@
   Renderbuffer* renderbuffer3 =
       renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient3Id);
   ASSERT_TRUE(renderbuffer3 != nullptr);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer3, kSamples3, kFormat3, kWidth3, kHeight3);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer3, kSamples3,
+                                              kFormat3, kWidth3, kHeight3);
   renderbuffer_manager_->SetCleared(renderbuffer3, true);
 
   framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, renderbuffer3);
@@ -334,8 +334,8 @@
   Renderbuffer* renderbuffer4 =
       renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient4Id);
   ASSERT_TRUE(renderbuffer4 != nullptr);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer4, kSamples4, kFormat4, kWidth4, kHeight4);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer4, kSamples4,
+                                              kFormat4, kWidth4, kHeight4);
   renderbuffer_manager_->SetCleared(renderbuffer4, true);
 
   EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
@@ -351,8 +351,8 @@
   EXPECT_TRUE(framebuffer_->IsCleared());
 
   // Check marking the renderbuffer as uncleared.
-  renderbuffer_manager_->SetInfo(
-      renderbuffer1, kSamples1, kFormat1, kWidth1, kHeight1);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
+                                              kFormat1, kWidth1, kHeight1);
   EXPECT_EQ(static_cast<GLenum>(kFormat1),
             framebuffer_->GetReadBufferInternalFormat());
   EXPECT_TRUE(framebuffer_->HasDepthAttachment());
@@ -385,8 +385,8 @@
   Renderbuffer* renderbuffer5 =
       renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient5Id);
   ASSERT_TRUE(renderbuffer5 != nullptr);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer5, kSamples5, kFormat5, kWidth5, kHeight5);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
+                                              kFormat5, kWidth5, kHeight5);
 
   framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, renderbuffer5);
   EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
@@ -422,18 +422,18 @@
   // Change samples.
   ASSERT_FALSE(
       feature_info_->feature_flags().chromium_framebuffer_mixed_samples);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer5, kDifferentSamples5, kFormat5, kWidth5, kHeight5);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kDifferentSamples5,
+                                              kFormat5, kWidth5, kHeight5);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE),
             framebuffer_->IsPossiblyComplete(feature_info_.get()));
-  renderbuffer_manager_->SetInfo(
-      renderbuffer5, kSamples5, kFormat5, kWidth5, kHeight5);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
+                                              kFormat5, kWidth5, kHeight5);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
             framebuffer_->IsPossiblyComplete(feature_info_.get()));
 
   // Check changing an attachment.
-  renderbuffer_manager_->SetInfo(
-      renderbuffer5, kSamples5, kFormat5, kWidth5 + 1, kHeight5);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
+                                              kFormat5, kWidth5 + 1, kHeight5);
 
   attachment = framebuffer_->GetAttachment(GL_STENCIL_ATTACHMENT);
   ASSERT_TRUE(attachment != nullptr);
@@ -448,8 +448,8 @@
 
   // Check removing it.
   // Restore the width of renderbuffer5 to avoid INCOMPLETE_DIMENSIONS_EXT.
-  renderbuffer_manager_->SetInfo(
-      renderbuffer5, kSamples5, kFormat5, kWidth5, kHeight5);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
+                                              kFormat5, kWidth5, kHeight5);
 
   framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, nullptr);
   EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
@@ -463,7 +463,8 @@
 
   // Remove depth, Set color to 0 size.
   framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, nullptr);
-  renderbuffer_manager_->SetInfo(renderbuffer1, kSamples1, kFormat1, 0, 0);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
+                                              kFormat1, 0, 0);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
             framebuffer_->IsPossiblyComplete(feature_info_.get()));
 
@@ -1578,8 +1579,8 @@
   Renderbuffer* renderbuffer1 =
       renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
   ASSERT_TRUE(renderbuffer1 != nullptr);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer1, kSamples1, kFormat1, kWidth1, kHeight1);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
+                                              kFormat1, kWidth1, kHeight1);
   framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, renderbuffer1);
 
   renderbuffer_manager_->CreateRenderbuffer(
@@ -1587,8 +1588,8 @@
   Renderbuffer* renderbuffer2 =
       renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient2Id);
   ASSERT_TRUE(renderbuffer2 != nullptr);
-  renderbuffer_manager_->SetInfo(
-      renderbuffer2, kSamples2, kFormat2, kWidth2, kHeight2);
+  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer2, kSamples2,
+                                              kFormat2, kWidth2, kHeight2);
   framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer2);
 
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT),
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 3d916f8..cf5da742e48 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -8524,11 +8524,8 @@
       }
     }
 
-    // TODO(gman): If renderbuffers tracked which framebuffers they were
-    // attached to we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
-    renderbuffer_manager()->SetInfo(
-        renderbuffer, samples, internalformat, width, height);
+    renderbuffer_manager()->SetInfoAndInvalidate(renderbuffer, samples,
+                                                 internalformat, width, height);
   }
 }
 
@@ -8563,11 +8560,8 @@
   }
   GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorageMultisampleEXT");
   if (error == GL_NO_ERROR) {
-    // TODO(gman): If renderbuffers tracked which framebuffers they were
-    // attached to we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
-    renderbuffer_manager()->SetInfo(
-        renderbuffer, samples, internalformat, width, height);
+    renderbuffer_manager()->SetInfoAndInvalidate(renderbuffer, samples,
+                                                 internalformat, width, height);
   }
 }
 
@@ -8717,11 +8711,8 @@
       height);
   GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorage");
   if (error == GL_NO_ERROR) {
-    // TODO(gman): If tetxures tracked which framebuffers they were attached to
-    // we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
-    renderbuffer_manager()->SetInfo(
-        renderbuffer, 0, internalformat, width, height);
+    renderbuffer_manager()->SetInfoAndInvalidate(renderbuffer, 0,
+                                                 internalformat, width, height);
   }
 }
 
@@ -19694,18 +19685,25 @@
       *static_cast<
           const volatile gles2::cmds::InitializeDiscardableTextureCHROMIUM*>(
           cmd_data);
-  TextureRef* texture = texture_manager()->GetTexture(c.texture_id);
+  GLuint texture_id = c.texture_id;
+  uint32_t shm_id = c.shm_id;
+  uint32_t shm_offset = c.shm_offset;
+
+  TextureRef* texture = texture_manager()->GetTexture(texture_id);
   if (!texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE,
                        "glInitializeDiscardableTextureCHROMIUM",
                        "Invalid texture ID");
     return error::kNoError;
   }
+  scoped_refptr<gpu::Buffer> buffer = GetSharedMemoryBuffer(shm_id);
+  if (!DiscardableHandleBase::ValidateParameters(buffer.get(), shm_offset))
+    return error::kInvalidArguments;
+
   size_t size = texture->texture()->estimated_size();
-  ServiceDiscardableHandle handle(GetSharedMemoryBuffer(c.shm_id), c.shm_offset,
-                                  c.shm_id);
+  ServiceDiscardableHandle handle(std::move(buffer), shm_offset, shm_id);
   GetContextGroup()->discardable_manager()->InsertLockedTexture(
-      c.texture_id, size, group_->texture_manager(), std::move(handle));
+      texture_id, size, group_->texture_manager(), std::move(handle));
   return error::kNoError;
 }
 
@@ -19716,11 +19714,12 @@
       *static_cast<
           const volatile gles2::cmds::UnlockDiscardableTextureCHROMIUM*>(
           cmd_data);
+  GLuint texture_id = c.texture_id;
   ServiceDiscardableManager* discardable_manager =
       GetContextGroup()->discardable_manager();
   TextureRef* texture_to_unbind;
-  if (!discardable_manager->UnlockTexture(
-          c.texture_id, group_->texture_manager(), &texture_to_unbind)) {
+  if (!discardable_manager->UnlockTexture(texture_id, group_->texture_manager(),
+                                          &texture_to_unbind)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glUnlockDiscardableTextureCHROMIUM",
                        "Texture ID not initialized");
   }
@@ -19736,8 +19735,9 @@
   const volatile gles2::cmds::LockDiscardableTextureCHROMIUM& c =
       *static_cast<const volatile gles2::cmds::LockDiscardableTextureCHROMIUM*>(
           cmd_data);
+  GLuint texture_id = c.texture_id;
   if (!GetContextGroup()->discardable_manager()->LockTexture(
-          c.texture_id, group_->texture_manager())) {
+          texture_id, group_->texture_manager())) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glLockDiscardableTextureCHROMIUM",
                        "Texture ID not initialized");
   }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index 15c9433..212a64f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -4621,6 +4621,34 @@
   EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
 }
 
+TEST_P(GLES2DecoderTest, TestInitDiscardableTextureWithInvalidArguments) {
+  EXPECT_EQ(0u, group().discardable_manager()->NumCacheEntriesForTesting());
+
+  // Manually initialize an init command with an invalid buffer.
+  {
+    cmds::InitializeDiscardableTextureCHROMIUM cmd;
+    cmd.Init(client_texture_id_, kInvalidSharedMemoryId, 0);
+    EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
+    EXPECT_EQ(0u, group().discardable_manager()->NumCacheEntriesForTesting());
+  }
+
+  // Manually initialize an init command with an out of bounds offset.
+  {
+    cmds::InitializeDiscardableTextureCHROMIUM cmd;
+    cmd.Init(client_texture_id_, shared_memory_id_, kInvalidSharedMemoryOffset);
+    EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
+    EXPECT_EQ(0u, group().discardable_manager()->NumCacheEntriesForTesting());
+  }
+
+  // Manually initialize an init command with a non-atomic32-aligned offset.
+  {
+    cmds::InitializeDiscardableTextureCHROMIUM cmd;
+    cmd.Init(client_texture_id_, shared_memory_id_, 1);
+    EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
+    EXPECT_EQ(0u, group().discardable_manager()->NumCacheEntriesForTesting());
+  }
+}
+
 TEST_P(GLES2DecoderTest, TestUnlockDiscardableTexture) {
   const ContextGroup& context_group = group();
   EXPECT_EQ(0u,
diff --git a/gpu/command_buffer/service/renderbuffer_manager.cc b/gpu/command_buffer/service/renderbuffer_manager.cc
index 5bc8e0e..d734547 100644
--- a/gpu/command_buffer/service/renderbuffer_manager.cc
+++ b/gpu/command_buffer/service/renderbuffer_manager.cc
@@ -15,6 +15,7 @@
 #include "base/trace_event/trace_event.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/framebuffer_manager.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "ui/gl/gl_implementation.h"
@@ -93,6 +94,20 @@
   return sizeof(RenderbufferTag) + sizeof(RenderbufferSignature);
 }
 
+void Renderbuffer::SetInfoAndInvalidate(GLsizei samples,
+                                        GLenum internalformat,
+                                        GLsizei width,
+                                        GLsizei height) {
+  samples_ = samples;
+  internal_format_ = internalformat;
+  width_ = width;
+  height_ = height;
+  cleared_ = false;
+  for (auto& point : framebuffer_attachment_points_) {
+    point.first->UnmarkAsComplete();
+  }
+}
+
 void Renderbuffer::AddToSignature(std::string* signature) const {
   DCHECK(signature);
   RenderbufferSignature signature_data(internal_format_,
@@ -120,6 +135,17 @@
   manager_->StartTracking(this);
 }
 
+void Renderbuffer::AddFramebufferAttachmentPoint(Framebuffer* framebuffer,
+                                                 GLenum attachment) {
+  framebuffer_attachment_points_.insert(
+      std::make_pair(framebuffer, attachment));
+}
+
+void Renderbuffer::RemoveFramebufferAttachmentPoint(Framebuffer* framebuffer,
+                                                    GLenum attachment) {
+  framebuffer_attachment_points_.erase(std::make_pair(framebuffer, attachment));
+}
+
 Renderbuffer::~Renderbuffer() {
   if (manager_) {
     if (manager_->have_context_) {
@@ -149,15 +175,17 @@
   memory_type_tracker_->TrackMemFree(renderbuffer->EstimatedSize());
 }
 
-void RenderbufferManager::SetInfo(
-    Renderbuffer* renderbuffer,
-    GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
+void RenderbufferManager::SetInfoAndInvalidate(Renderbuffer* renderbuffer,
+                                               GLsizei samples,
+                                               GLenum internalformat,
+                                               GLsizei width,
+                                               GLsizei height) {
   DCHECK(renderbuffer);
   if (!renderbuffer->cleared()) {
     --num_uncleared_renderbuffers_;
   }
   memory_type_tracker_->TrackMemFree(renderbuffer->EstimatedSize());
-  renderbuffer->SetInfo(samples, internalformat, width, height);
+  renderbuffer->SetInfoAndInvalidate(samples, internalformat, width, height);
   memory_type_tracker_->TrackMemAlloc(renderbuffer->EstimatedSize());
   if (!renderbuffer->cleared()) {
     ++num_uncleared_renderbuffers_;
diff --git a/gpu/command_buffer/service/renderbuffer_manager.h b/gpu/command_buffer/service/renderbuffer_manager.h
index f808e96..698a1d9 100644
--- a/gpu/command_buffer/service/renderbuffer_manager.h
+++ b/gpu/command_buffer/service/renderbuffer_manager.h
@@ -11,6 +11,7 @@
 #include <memory>
 #include <string>
 
+#include "base/containers/flat_set.h"
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -22,6 +23,7 @@
 namespace gles2 {
 
 class FeatureInfo;
+class Framebuffer;
 class RenderbufferManager;
 
 // Info about a Renderbuffer.
@@ -72,6 +74,11 @@
     return has_been_bound_ && !IsDeleted();
   }
 
+  void AddFramebufferAttachmentPoint(Framebuffer* framebuffer,
+                                     GLenum attachment);
+  void RemoveFramebufferAttachmentPoint(Framebuffer* framebuffer,
+                                        GLenum attachment);
+
   size_t EstimatedSize();
 
   size_t GetSignatureSize() const;
@@ -87,14 +94,10 @@
     cleared_ = cleared;
   }
 
-  void SetInfo(
-      GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
-    samples_ = samples;
-    internal_format_ = internalformat;
-    width_ = width;
-    height_ = height;
-    cleared_ = false;
-  }
+  void SetInfoAndInvalidate(GLsizei samples,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height);
 
   void MarkAsDeleted() {
     client_id_ = 0;
@@ -124,6 +127,11 @@
   // Dimensions of renderbuffer.
   GLsizei width_;
   GLsizei height_;
+
+  // Framebuffer objects that this renderbuffer is attached to
+  // (client ID, attachment).
+  base::flat_set<std::pair<Framebuffer*, GLenum>>
+      framebuffer_attachment_points_;
 };
 
 // This class keeps track of the renderbuffers and whether or not they have
@@ -149,9 +157,11 @@
     return num_uncleared_renderbuffers_ != 0;
   }
 
-  void SetInfo(
-      Renderbuffer* renderbuffer,
-      GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+  void SetInfoAndInvalidate(Renderbuffer* renderbuffer,
+                            GLsizei samples,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height);
 
   void SetCleared(Renderbuffer* renderbuffer, bool cleared);
 
diff --git a/gpu/command_buffer/service/renderbuffer_manager_unittest.cc b/gpu/command_buffer/service/renderbuffer_manager_unittest.cc
index f1c45ae..24318fc2 100644
--- a/gpu/command_buffer/service/renderbuffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/renderbuffer_manager_unittest.cc
@@ -155,7 +155,8 @@
   const GLenum kFormat = GL_RGBA4;
   const GLsizei kWidth = 128;
   const GLsizei kHeight = 64;
-  manager_->SetInfo(renderbuffer1, kSamples, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1, kSamples, kFormat, kWidth,
+                                 kHeight);
   EXPECT_EQ(kSamples, renderbuffer1->samples());
   EXPECT_EQ(kFormat, renderbuffer1->internal_format());
   EXPECT_EQ(kWidth, renderbuffer1->width());
@@ -169,7 +170,8 @@
   EXPECT_TRUE(renderbuffer1->cleared());
   EXPECT_FALSE(manager_->HaveUnclearedRenderbuffers());
 
-  manager_->SetInfo(renderbuffer1, kSamples, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1, kSamples, kFormat, kWidth,
+                                 kHeight);
   EXPECT_TRUE(manager_->HaveUnclearedRenderbuffers());
 
   // Check that the renderbuffer is deleted when the last ref is released.
@@ -201,10 +203,12 @@
   manager_->ComputeEstimatedRenderbufferSize(
       kWidth, kHeight2, kSamples, kFormat, &expected_size_2);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, expected_size_1);
-  manager_->SetInfo(renderbuffer1, kSamples, kFormat, kWidth, kHeight1);
+  manager_->SetInfoAndInvalidate(renderbuffer1, kSamples, kFormat, kWidth,
+                                 kHeight1);
   EXPECT_MEMORY_ALLOCATION_CHANGE(expected_size_1, 0);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, expected_size_2);
-  manager_->SetInfo(renderbuffer1, kSamples, kFormat, kWidth, kHeight2);
+  manager_->SetInfoAndInvalidate(renderbuffer1, kSamples, kFormat, kWidth,
+                                 kHeight2);
   EXPECT_MEMORY_ALLOCATION_CHANGE(expected_size_2, 0);
   EXPECT_CALL(*gl_, DeleteRenderbuffersEXT(1, ::testing::Pointee(kService1Id)))
       .Times(1)
@@ -225,7 +229,8 @@
   const GLenum kFormat = GL_RGBA4;
   const GLsizei kWidth = 128;
   const GLsizei kHeight = 64;
-  manager_->SetInfo(renderbuffer1.get(), kSamples, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat, kWidth,
+                                 kHeight);
   // See that it still affects manager.
   EXPECT_TRUE(manager_->HaveUnclearedRenderbuffers());
   manager_->SetCleared(renderbuffer1.get(), true);
@@ -258,7 +263,8 @@
   const GLenum kFormat = GL_RGBA4;
   const GLsizei kWidth = 128;
   const GLsizei kHeight = 64;
-  manager_->SetInfo(renderbuffer1.get(), kSamples, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat, kWidth,
+                                 kHeight);
   std::string signature1;
   std::string signature2;
   renderbuffer1->AddToSignature(&signature1);
@@ -267,31 +273,32 @@
   EXPECT_FALSE(InSet(&string_set, signature1));
 
   // change things and see that the signatures change.
-  manager_->SetInfo(
-      renderbuffer1.get(), kSamples + 1, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples + 1, kFormat,
+                                 kWidth, kHeight);
   renderbuffer1->AddToSignature(&signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetInfo(
-      renderbuffer1.get(), kSamples, kFormat + 1, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat + 1,
+                                 kWidth, kHeight);
   signature2.clear();
   renderbuffer1->AddToSignature(&signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetInfo(
-      renderbuffer1.get(), kSamples, kFormat, kWidth + 1, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat,
+                                 kWidth + 1, kHeight);
   signature2.clear();
   renderbuffer1->AddToSignature(&signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetInfo(
-      renderbuffer1.get(), kSamples, kFormat, kWidth, kHeight + 1);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat, kWidth,
+                                 kHeight + 1);
   signature2.clear();
   renderbuffer1->AddToSignature(&signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
   // put it back to the same and it should be the same.
-  manager_->SetInfo(renderbuffer1.get(), kSamples, kFormat, kWidth, kHeight);
+  manager_->SetInfoAndInvalidate(renderbuffer1.get(), kSamples, kFormat, kWidth,
+                                 kHeight);
   signature2.clear();
   renderbuffer1->AddToSignature(&signature2);
   EXPECT_EQ(signature1, signature2);
diff --git a/ios/chrome/browser/net/crl_set_fetcher.cc b/ios/chrome/browser/net/crl_set_fetcher.cc
index 92966ca6..9f1ea83 100644
--- a/ios/chrome/browser/net/crl_set_fetcher.cc
+++ b/ios/chrome/browser/net/crl_set_fetcher.cc
@@ -161,11 +161,11 @@
 }
 
 update_client::CrxInstaller::Result CRLSetFetcher::Install(
-    const base::DictionaryValue& manifest,
+    std::unique_ptr<base::DictionaryValue> manifest,
     const base::FilePath& unpack_path) {
   const auto result = update_client::InstallFunctionWrapper(
       base::Bind(&CRLSetFetcher::DoInstall, base::Unretained(this),
-                 base::ConstRef(manifest), base::ConstRef(unpack_path)));
+                 base::ConstRef(*manifest), base::ConstRef(unpack_path)));
   base::DeleteFile(unpack_path, true /* recursive */);
   return result;
 }
diff --git a/ios/chrome/browser/net/crl_set_fetcher.h b/ios/chrome/browser/net/crl_set_fetcher.h
index 88821a9..ad3ea1a 100644
--- a/ios/chrome/browser/net/crl_set_fetcher.h
+++ b/ios/chrome/browser/net/crl_set_fetcher.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/compiler_specific.h"
@@ -40,7 +41,7 @@
 
   // ComponentInstaller interface
   void OnUpdateError(int error) override;
-  Result Install(const base::DictionaryValue& manifest,
+  Result Install(std::unique_ptr<base::DictionaryValue> manifest,
                  const base::FilePath& unpack_path) override;
   bool GetInstalledFile(const std::string& file,
                         base::FilePath* installed_file) override;
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index 17a93a4..f58c9d5d 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -83,10 +83,12 @@
     "offline_page_native_content_unittest.mm",
     "reading_list_collection_view_controller_unittest.mm",
     "reading_list_coordinator_unittest.mm",
+    "reading_list_mediator_unittest.mm",
   ]
   deps = [
     ":reading_list",
     "//base",
+    "//base/test:test_support",
     "//components/favicon/core",
     "//components/favicon/core/test:test_support",
     "//components/prefs",
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm b/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm
new file mode 100644
index 0000000..2394879
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/reading_list/reading_list_mediator.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/simple_test_clock.h"
+#include "components/reading_list/core/reading_list_model_impl.h"
+#include "components/url_formatter/url_formatter.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class ReadingListMediatorTest : public PlatformTest {
+ public:
+  ReadingListMediatorTest() {
+    std::unique_ptr<base::SimpleTestClock> clock =
+        base::MakeUnique<base::SimpleTestClock>();
+    clock_ = clock.get();
+    model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr,
+                                                    std::move(clock));
+
+    no_title_entry_url_ = GURL("http://chromium.org/unread3");
+    // The first 3 have the same update time on purpose.
+    model_->AddEntry(GURL("http://chromium.org/unread1"), "unread1",
+                     reading_list::ADDED_VIA_CURRENT_APP);
+    model_->AddEntry(GURL("http://chromium.org/read1"), "read1",
+                     reading_list::ADDED_VIA_CURRENT_APP);
+    model_->SetReadStatus(GURL("http://chromium.org/read1"), true);
+    model_->AddEntry(GURL("http://chromium.org/unread2"), "unread2",
+                     reading_list::ADDED_VIA_CURRENT_APP);
+    clock_->Advance(base::TimeDelta::FromMilliseconds(10));
+    model_->AddEntry(no_title_entry_url_, "",
+                     reading_list::ADDED_VIA_CURRENT_APP);
+    clock_->Advance(base::TimeDelta::FromMilliseconds(10));
+    model_->AddEntry(GURL("http://chromium.org/read2"), "read2",
+                     reading_list::ADDED_VIA_CURRENT_APP);
+    model_->SetReadStatus(GURL("http://chromium.org/read2"), true);
+
+    mediator_ = [[ReadingListMediator alloc] initWithModel:model_.get()];
+  }
+
+ protected:
+  std::unique_ptr<ReadingListModelImpl> model_;
+  ReadingListMediator* mediator_;
+  base::SimpleTestClock* clock_;
+  GURL no_title_entry_url_;
+};
+
+TEST_F(ReadingListMediatorTest, fillItems) {
+  // Setup.
+  NSMutableArray<ReadingListCollectionViewItem*>* readArray =
+      [NSMutableArray array];
+  NSMutableArray<ReadingListCollectionViewItem*>* unreadArray =
+      [NSMutableArray array];
+
+  // Action.
+  [mediator_ fillReadItems:readArray unreadItems:unreadArray];
+
+  // Tests.
+  EXPECT_EQ(3U, [unreadArray count]);
+  EXPECT_EQ(2U, [readArray count]);
+  EXPECT_TRUE([unreadArray[0].title
+      isEqualToString:base::SysUTF16ToNSString(url_formatter::FormatUrl(
+                          no_title_entry_url_.GetOrigin()))]);
+  EXPECT_TRUE([readArray[0].title isEqualToString:@"read2"]);
+  EXPECT_TRUE([readArray[1].title isEqualToString:@"read1"]);
+}
diff --git a/ios/web/public/test/fakes/BUILD.gn b/ios/web/public/test/fakes/BUILD.gn
index 6746ba4..642b01c0 100644
--- a/ios/web/public/test/fakes/BUILD.gn
+++ b/ios/web/public/test/fakes/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("fakes") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
 
   deps = [
diff --git a/ios/web/public/test/fakes/crw_test_js_injection_receiver.mm b/ios/web/public/test/fakes/crw_test_js_injection_receiver.mm
index 7ab4421..e97bd620 100644
--- a/ios/web/public/test/fakes/crw_test_js_injection_receiver.mm
+++ b/ios/web/public/test/fakes/crw_test_js_injection_receiver.mm
@@ -7,16 +7,18 @@
 #import <UIKit/UIKit.h>
 #import <WebKit/WebKit.h>
 
-#import "base/ios/weak_nsobject.h"
-#import "base/mac/scoped_nsobject.h"
 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h"
 #import "ios/web/web_state/ui/web_view_js_utils.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface CRWTestWKWebViewEvaluator : NSObject<CRWJSInjectionEvaluator> {
   // Web view for JavaScript evaluation.
-  base::scoped_nsobject<WKWebView> _webView;
+  WKWebView* _webView;
   // Set to track injected script managers.
-  base::scoped_nsobject<NSMutableSet> _injectedScriptManagers;
+  NSMutableSet* _injectedScriptManagers;
 }
 @end
 
@@ -24,8 +26,8 @@
 
 - (instancetype)init {
   if (self = [super init]) {
-    _webView.reset([[WKWebView alloc] init]);
-    _injectedScriptManagers.reset([[NSMutableSet alloc] init]);
+    _webView = [[WKWebView alloc] init];
+    _injectedScriptManagers = [[NSMutableSet alloc] init];
   }
   return self;
 }
@@ -50,17 +52,17 @@
 @end
 
 @interface CRWTestJSInjectionReceiver () {
-  base::scoped_nsobject<CRWTestWKWebViewEvaluator> evaluator_;
+  CRWTestWKWebViewEvaluator* evaluator_;
 }
 @end
 
 @implementation CRWTestJSInjectionReceiver
 
 - (id)init {
-  base::scoped_nsobject<CRWTestWKWebViewEvaluator> evaluator(
-      [[CRWTestWKWebViewEvaluator alloc] init]);
+  CRWTestWKWebViewEvaluator* evaluator =
+      [[CRWTestWKWebViewEvaluator alloc] init];
   if (self = [super initWithEvaluator:evaluator])
-    evaluator_.swap(evaluator);
+    evaluator_ = evaluator;
   return self;
 }
 
diff --git a/ios/web/public/test/fakes/crw_test_web_state_observer.mm b/ios/web/public/test/fakes/crw_test_web_state_observer.mm
index e133138..2d806c22 100644
--- a/ios/web/public/test/fakes/crw_test_web_state_observer.mm
+++ b/ios/web/public/test/fakes/crw_test_web_state_observer.mm
@@ -10,6 +10,10 @@
 #include "net/http/http_response_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 TestFormActivityInfo::TestFormActivityInfo() {}
 TestFormActivityInfo::~TestFormActivityInfo() = default;
diff --git a/ios/web/public/test/fakes/test_java_script_dialog_presenter.h b/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
index 8abb853..d6ab838 100644
--- a/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
+++ b/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #import "ios/web/public/java_script_dialog_presenter.h"
-#import "base/mac/scoped_nsobject.h"
 
 namespace web {
 
@@ -19,8 +18,8 @@
   WebState* web_state = nullptr;
   GURL origin_url;
   JavaScriptDialogType java_script_dialog_type;
-  base::scoped_nsobject<NSString> message_text;
-  base::scoped_nsobject<NSString> default_prompt_text;
+  NSString* message_text;
+  NSString* default_prompt_text;
 };
 
 // Test presenter to check that the JavaScriptDialogPresenter methods are called
@@ -55,14 +54,14 @@
 
   // Sets |user_input| argument to be used for RunJavaScriptDialog callback.
   void set_callback_user_input_argument(NSString* user_input) {
-    callback_user_input_argument_.reset(user_input);
+    callback_user_input_argument_ = user_input;
   }
 
  private:
   bool cancel_dialogs_called_ = false;
   std::vector<TestJavaScriptDialog> requested_dialogs_;
   bool callback_success_argument_ = false;
-  base::scoped_nsobject<NSString> callback_user_input_argument_;
+  NSString* callback_user_input_argument_;
 };
 
 }  // namespace web
diff --git a/ios/web/public/test/fakes/test_java_script_dialog_presenter.mm b/ios/web/public/test/fakes/test_java_script_dialog_presenter.mm
index 2e44213..2403a03 100644
--- a/ios/web/public/test/fakes/test_java_script_dialog_presenter.mm
+++ b/ios/web/public/test/fakes/test_java_script_dialog_presenter.mm
@@ -4,6 +4,10 @@
 
 #import "ios/web/public/test/fakes/test_java_script_dialog_presenter.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 TestJavaScriptDialog::TestJavaScriptDialog() = default;
@@ -28,12 +32,12 @@
   dialog.web_state = web_state;
   dialog.origin_url = origin_url;
   dialog.java_script_dialog_type = java_script_dialog_type;
-  dialog.message_text.reset([message_text copy]);
-  dialog.default_prompt_text.reset([default_prompt_text copy]);
+  dialog.message_text = [message_text copy];
+  dialog.default_prompt_text = [default_prompt_text copy];
 
   requested_dialogs_.push_back(dialog);
 
-  callback.Run(callback_success_argument_, callback_user_input_argument_.get());
+  callback.Run(callback_success_argument_, callback_user_input_argument_);
 }
 
 void TestJavaScriptDialogPresenter::CancelDialogs(WebState* web_state) {
diff --git a/ios/web/public/test/fakes/test_native_content.mm b/ios/web/public/test/fakes/test_native_content.mm
index a3c1e76..09455432 100644
--- a/ios/web/public/test/fakes/test_native_content.mm
+++ b/ios/web/public/test/fakes/test_native_content.mm
@@ -4,12 +4,14 @@
 
 #import "ios/web/public/test/fakes/test_native_content.h"
 
-#import "base/mac/scoped_nsobject.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 @implementation TestNativeContent {
   GURL _URL;
   GURL _virtualURL;
-  base::scoped_nsobject<UIView> _view;
+  UIView* _view;
 }
 - (instancetype)initWithURL:(const GURL&)URL
                  virtualURL:(const GURL&)virtualURL {
diff --git a/ios/web/public/test/fakes/test_native_content_provider.mm b/ios/web/public/test/fakes/test_native_content_provider.mm
index 4a026aec..5697b53 100644
--- a/ios/web/public/test/fakes/test_native_content_provider.mm
+++ b/ios/web/public/test/fakes/test_native_content_provider.mm
@@ -8,6 +8,10 @@
 
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @implementation TestNativeContentProvider {
   std::map<GURL, id<CRWNativeContent>> _nativeContent;
 }
diff --git a/ios/web/public/test/fakes/test_navigation_manager.mm b/ios/web/public/test/fakes/test_navigation_manager.mm
index c1c0125..ada74383 100644
--- a/ios/web/public/test/fakes/test_navigation_manager.mm
+++ b/ios/web/public/test/fakes/test_navigation_manager.mm
@@ -4,6 +4,10 @@
 
 #import "ios/web/public/test/fakes/test_navigation_manager.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 TestNavigationManager::TestNavigationManager()
diff --git a/ios/web/public/test/fakes/test_web_client.h b/ios/web/public/test/fakes/test_web_client.h
index 9e65bf9..4d572f9 100644
--- a/ios/web/public/test/fakes/test_web_client.h
+++ b/ios/web/public/test/fakes/test_web_client.h
@@ -8,7 +8,6 @@
 #import <Foundation/Foundation.h>
 
 #include "base/compiler_specific.h"
-#import "base/mac/scoped_nsobject.h"
 #import "ios/web/public/web_client.h"
 #include "net/ssl/ssl_info.h"
 #include "url/gurl.h"
@@ -50,7 +49,7 @@
   bool last_cert_error_overridable() { return last_cert_error_overridable_; }
 
  private:
-  base::scoped_nsobject<NSString> early_page_script_;
+  NSString* early_page_script_;
   // Last arguments passed to AllowCertificateError.
   int last_cert_error_code_;
   net::SSLInfo last_cert_error_ssl_info_;
diff --git a/ios/web/public/test/fakes/test_web_client.mm b/ios/web/public/test/fakes/test_web_client.mm
index aac1069..c611433 100644
--- a/ios/web/public/test/fakes/test_web_client.mm
+++ b/ios/web/public/test/fakes/test_web_client.mm
@@ -9,6 +9,10 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 TestWebClient::TestWebClient()
@@ -40,11 +44,11 @@
 }
 
 NSString* TestWebClient::GetEarlyPageScript(BrowserState* browser_state) const {
-  return early_page_script_ ? early_page_script_.get() : @"";
+  return early_page_script_ ? early_page_script_ : @"";
 }
 
 void TestWebClient::SetEarlyPageScript(NSString* page_script) {
-  early_page_script_.reset([page_script copy]);
+  early_page_script_ = [page_script copy];
 }
 
 void TestWebClient::AllowCertificateError(
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index bcd298b..d98f99e5 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -100,7 +100,7 @@
   BrowserState* browser_state_;
   bool web_usage_enabled_;
   bool is_loading_;
-  base::scoped_nsobject<CRWContentView> transient_content_view_;
+  CRWContentView* transient_content_view_;
   GURL url_;
   base::string16 title_;
   URLVerificationTrustLevel trust_level_;
@@ -108,7 +108,7 @@
   std::string mime_type_;
   std::string content_language_;
   std::unique_ptr<NavigationManager> navigation_manager_;
-  base::scoped_nsobject<UIView> view_;
+  UIView* view_;
 
   // A list of observers notified when page state changes. Weak references.
   base::ObserverList<WebStateObserver, true> observers_;
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index ac04032..9c268874 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -10,6 +10,10 @@
 #import "ios/web/public/web_state/ui/crw_content_view.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 void TestWebState::AddObserver(WebStateObserver* observer) {
@@ -59,7 +63,7 @@
 void TestWebState::SetShouldSuppressDialogs(bool should_suppress) {}
 
 UIView* TestWebState::GetView() {
-  return view_.get();
+  return view_;
 }
 
 const NavigationManager* TestWebState::GetNavigationManager() const {
@@ -90,7 +94,7 @@
 }
 
 void TestWebState::SetView(UIView* view) {
-  view_.reset([view retain]);
+  view_ = view;
 }
 
 CRWJSInjectionReceiver* TestWebState::GetJSInjectionReceiver() const {
@@ -194,16 +198,16 @@
 
 void TestWebState::ShowTransientContentView(CRWContentView* content_view) {
   if (content_view) {
-    transient_content_view_.reset([content_view retain]);
+    transient_content_view_ = content_view;
   }
 }
 
 void TestWebState::ClearTransientContentView() {
-  transient_content_view_.reset();
+  transient_content_view_ = nil;
 }
 
 CRWContentView* TestWebState::GetTransientContentView() {
-  return transient_content_view_.get();
+  return transient_content_view_;
 }
 
 void TestWebState::SetCurrentURL(const GURL& url) {
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.h b/ios/web/public/test/fakes/test_web_state_delegate.h
index a63f000..a7d63d6e 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.h
+++ b/ios/web/public/test/fakes/test_web_state_delegate.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <set>
 
-#include "base/mac/scoped_nsobject.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
 #import "ios/web/public/test/fakes/test_java_script_dialog_presenter.h"
 
@@ -53,8 +52,8 @@
   TestAuthenticationRequest(const TestAuthenticationRequest&);
   ~TestAuthenticationRequest();
   WebState* web_state = nullptr;
-  base::scoped_nsobject<NSURLProtectionSpace> protection_space;
-  base::scoped_nsobject<NSURLCredential> credential;
+  NSURLProtectionSpace* protection_space;
+  NSURLCredential* credential;
   WebStateDelegate::AuthCallback auth_callback;
 };
 
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.mm b/ios/web/public/test/fakes/test_web_state_delegate.mm
index 23f18c4..4e54532 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.mm
+++ b/ios/web/public/test/fakes/test_web_state_delegate.mm
@@ -6,6 +6,10 @@
 
 #include "base/memory/ptr_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 TestOpenURLRequest::TestOpenURLRequest()
@@ -118,9 +122,8 @@
     const AuthCallback& callback) {
   last_authentication_request_ = base::MakeUnique<TestAuthenticationRequest>();
   last_authentication_request_->web_state = source;
-  last_authentication_request_->protection_space.reset(
-      [protection_space retain]);
-  last_authentication_request_->credential.reset([credential retain]);
+  last_authentication_request_->protection_space = protection_space;
+  last_authentication_request_->credential = credential;
   last_authentication_request_->auth_callback = callback;
 }
 
diff --git a/ios/web/public/test/fakes/test_web_state_observer.mm b/ios/web/public/test/fakes/test_web_state_observer.mm
index c184f6e..3d9c96b 100644
--- a/ios/web/public/test/fakes/test_web_state_observer.mm
+++ b/ios/web/public/test/fakes/test_web_state_observer.mm
@@ -11,6 +11,10 @@
 #include "net/http/http_response_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 
 TestWebStateObserver::TestWebStateObserver(WebState* web_state)
diff --git a/ios/web/public/test/fakes/test_web_state_observer_util.mm b/ios/web/public/test/fakes/test_web_state_observer_util.mm
index b297dcb..fcd81fd9 100644
--- a/ios/web/public/test/fakes/test_web_state_observer_util.mm
+++ b/ios/web/public/test/fakes/test_web_state_observer_util.mm
@@ -6,6 +6,10 @@
 
 #import "ios/web/public/web_state/navigation_context.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace web {
 TestDidStartNavigationInfo::TestDidStartNavigationInfo() {}
 TestDidStartNavigationInfo::~TestDidStartNavigationInfo() = default;
diff --git a/ios/web/public/test/fakes/test_web_view_content_view.mm b/ios/web/public/test/fakes/test_web_view_content_view.mm
index a5c08161..0c4506a 100644
--- a/ios/web/public/test/fakes/test_web_view_content_view.mm
+++ b/ios/web/public/test/fakes/test_web_view_content_view.mm
@@ -5,11 +5,14 @@
 #import "ios/web/public/test/fakes/test_web_view_content_view.h"
 
 #include "base/logging.h"
-#import "base/mac/scoped_nsobject.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 @interface TestWebViewContentView () {
-  base::scoped_nsprotocol<id> _mockWebView;
-  base::scoped_nsprotocol<id> _mockScrollView;
+  id _mockWebView;
+  id _mockScrollView;
 }
 
 @end
@@ -21,8 +24,8 @@
   if (self) {
     DCHECK(webView);
     DCHECK(scrollView);
-    _mockWebView.reset([webView retain]);
-    _mockScrollView.reset([scrollView retain]);
+    _mockWebView = webView;
+    _mockScrollView = scrollView;
   }
   return self;
 }
@@ -40,11 +43,11 @@
 #pragma mark Accessors
 
 - (UIScrollView*)scrollView {
-  return static_cast<UIScrollView*>(_mockScrollView.get());
+  return static_cast<UIScrollView*>(_mockScrollView);
 }
 
 - (UIView*)webView {
-  return static_cast<UIView*>(_mockWebView.get());
+  return static_cast<UIView*>(_mockWebView);
 }
 
 @end
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index 9eea543..3a5d21b 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -228,6 +228,7 @@
                                   key_systems_properties) override;
   bool IsSupportedAudioConfig(const media::AudioConfig& config) final;
   bool IsSupportedVideoConfig(const media::VideoConfig& config) final;
+  bool IsSupportedBitstreamAudioCodec(AudioCodec codec) final;
 
   // Helper function to test the case where IsKeySystemsUpdateNeeded() is true
   // after AddSupportedKeySystems() is called.
@@ -273,6 +274,10 @@
   return true;
 }
 
+bool TestMediaClient::IsSupportedBitstreamAudioCodec(AudioCodec codec) {
+  return false;
+}
+
 void TestMediaClient::SetKeySystemsUpdateNeeded() {
   is_update_needed_ = true;
 }
diff --git a/media/base/media_client.h b/media/base/media_client.h
index 0ebafd3..0b1bd6b 100644
--- a/media/base/media_client.h
+++ b/media/base/media_client.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "media/base/audio_codecs.h"
 #include "media/base/decode_capabilities.h"
 #include "media/base/key_system_properties.h"
 #include "media/base/media_export.h"
@@ -52,6 +53,10 @@
 
   // Returns true if the given video config is supported.
   virtual bool IsSupportedVideoConfig(const VideoConfig& config) = 0;
+
+  // Returns true if the compressed audio |codec| format is supported by the
+  // audio sink.
+  virtual bool IsSupportedBitstreamAudioCodec(AudioCodec codec) = 0;
 };
 
 }  // namespace media
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index af8c995..d25ae19 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -138,10 +138,6 @@
 const char kDisableRTCSmoothnessAlgorithm[] =
     "disable-rtc-smoothness-algorithm";
 
-// Enables demuxing of vp9 in mp4. Note that this flag will not have any effect
-// if MP4 demuxing is not enabled in the build.
-const char kEnableVp9InMp4[] = "enable-vp9-in-mp4";
-
 // Force media player using SurfaceView instead of SurfaceTexture on Android.
 const char kForceVideoOverlays[] = "force-video-overlays";
 
@@ -204,6 +200,10 @@
 #endif
 };
 
+// Display the Cast overlay button on the media controls.
+const base::Feature kMediaCastOverlayButton{"MediaCastOverlayButton",
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Use AndroidOverlay rather than ContentVideoView in clank?
 const base::Feature kUseAndroidOverlay{"use-android_overlay",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index abbeb5a8..8ba5087 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -77,8 +77,6 @@
 
 MEDIA_EXPORT extern const char kDisableRTCSmoothnessAlgorithm[];
 
-MEDIA_EXPORT extern const char kEnableVp9InMp4[];
-
 MEDIA_EXPORT extern const char kForceVideoOverlays[];
 
 MEDIA_EXPORT extern const char kMSEAudioBufferSizeLimit[];
@@ -110,6 +108,7 @@
 MEDIA_EXPORT extern const base::Feature kBackgroundVideoTrackOptimization;
 MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting;
 MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream;
+MEDIA_EXPORT extern const base::Feature kMediaCastOverlayButton;
 MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC;
 MEDIA_EXPORT extern const base::Feature kMojoCdm;
 MEDIA_EXPORT extern const base::Feature kNewAudioRenderingMixingStrategy;
diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc
index aa5bea1..4a263d0 100644
--- a/media/base/mime_util_internal.cc
+++ b/media/base/mime_util_internal.cc
@@ -89,12 +89,9 @@
                             uint8_t* out_level,
                             VideoColorSpace* out_color_space) {
   if (mime_type_lower_case == "video/mp4") {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kEnableVp9InMp4)) {
-      // Only new style is allowed for mp4.
-      return ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
-                                     out_color_space);
-    }
+    // Only new style is allowed for mp4.
+    return ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
+                                   out_color_space);
   } else if (mime_type_lower_case == "video/webm") {
     if (HasNewVp9CodecStringSupport() &&
         ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
diff --git a/media/base/watch_time_keys.cc b/media/base/watch_time_keys.cc
index 6ead6c2..f473039b 100644
--- a/media/base/watch_time_keys.cc
+++ b/media/base/watch_time_keys.cc
@@ -15,6 +15,10 @@
 const char kWatchTimeAudioVideoAc[] = "Media.WatchTime.AudioVideo.AC";
 const char kWatchTimeAudioVideoEmbeddedExperience[] =
     "Media.WatchTime.AudioVideo.EmbeddedExperience";
+const char kWatchTimeAudioVideoNativeControlsOn[] =
+    "Media.WatchTime.AudioVideo.NativeControlsOn";
+const char kWatchTimeAudioVideoNativeControlsOff[] =
+    "Media.WatchTime.AudioVideo.NativeControlsOff";
 
 // Audio only "watch time" metrics.
 const char kWatchTimeAudioAll[] = "Media.WatchTime.Audio.All";
@@ -25,6 +29,10 @@
 const char kWatchTimeAudioAc[] = "Media.WatchTime.Audio.AC";
 const char kWatchTimeAudioEmbeddedExperience[] =
     "Media.WatchTime.Audio.EmbeddedExperience";
+const char kWatchTimeAudioNativeControlsOn[] =
+    "Media.WatchTime.Audio.NativeControlsOn";
+const char kWatchTimeAudioNativeControlsOff[] =
+    "Media.WatchTime.Audio.NativeControlsOff";
 
 // Audio+video background watch time metrics.
 const char kWatchTimeAudioVideoBackgroundAll[] =
@@ -44,6 +52,7 @@
 
 const char kWatchTimeFinalize[] = "FinalizeWatchTime";
 const char kWatchTimeFinalizePower[] = "FinalizePowerWatchTime";
+const char kWatchTimeFinalizeControls[] = "FinalizeControlsWatchTime";
 
 const char kWatchTimeUnderflowCount[] = "UnderflowCount";
 
@@ -62,27 +71,33 @@
 
 base::flat_set<base::StringPiece> GetWatchTimeKeys() {
   return base::flat_set<base::StringPiece>(
-      {kWatchTimeAudioAll,
-       kWatchTimeAudioMse,
-       kWatchTimeAudioEme,
-       kWatchTimeAudioSrc,
-       kWatchTimeAudioBattery,
-       kWatchTimeAudioAc,
-       kWatchTimeAudioEmbeddedExperience,
-       kWatchTimeAudioVideoAll,
-       kWatchTimeAudioVideoMse,
-       kWatchTimeAudioVideoEme,
-       kWatchTimeAudioVideoSrc,
-       kWatchTimeAudioVideoBattery,
-       kWatchTimeAudioVideoAc,
-       kWatchTimeAudioVideoEmbeddedExperience,
-       kWatchTimeAudioVideoBackgroundAll,
-       kWatchTimeAudioVideoBackgroundMse,
-       kWatchTimeAudioVideoBackgroundEme,
-       kWatchTimeAudioVideoBackgroundSrc,
-       kWatchTimeAudioVideoBackgroundBattery,
-       kWatchTimeAudioVideoBackgroundAc,
-       kWatchTimeAudioVideoBackgroundEmbeddedExperience},
+      {
+          kWatchTimeAudioAll,
+          kWatchTimeAudioMse,
+          kWatchTimeAudioEme,
+          kWatchTimeAudioSrc,
+          kWatchTimeAudioBattery,
+          kWatchTimeAudioAc,
+          kWatchTimeAudioEmbeddedExperience,
+          kWatchTimeAudioNativeControlsOn,
+          kWatchTimeAudioNativeControlsOff,
+          kWatchTimeAudioVideoAll,
+          kWatchTimeAudioVideoMse,
+          kWatchTimeAudioVideoEme,
+          kWatchTimeAudioVideoSrc,
+          kWatchTimeAudioVideoBattery,
+          kWatchTimeAudioVideoAc,
+          kWatchTimeAudioVideoEmbeddedExperience,
+          kWatchTimeAudioVideoNativeControlsOn,
+          kWatchTimeAudioVideoNativeControlsOff,
+          kWatchTimeAudioVideoBackgroundAll,
+          kWatchTimeAudioVideoBackgroundMse,
+          kWatchTimeAudioVideoBackgroundEme,
+          kWatchTimeAudioVideoBackgroundSrc,
+          kWatchTimeAudioVideoBackgroundBattery,
+          kWatchTimeAudioVideoBackgroundAc,
+          kWatchTimeAudioVideoBackgroundEmbeddedExperience,
+      },
       base::KEEP_FIRST_OF_DUPES);
 }
 
@@ -94,4 +109,12 @@
       base::KEEP_FIRST_OF_DUPES);
 }
 
+base::flat_set<base::StringPiece> GetWatchTimeControlsKeys() {
+  return base::flat_set<base::StringPiece>(
+      {kWatchTimeAudioNativeControlsOn, kWatchTimeAudioNativeControlsOff,
+       kWatchTimeAudioVideoNativeControlsOn,
+       kWatchTimeAudioVideoNativeControlsOff},
+      base::KEEP_FIRST_OF_DUPES);
+}
+
 }  // namespace media
diff --git a/media/base/watch_time_keys.h b/media/base/watch_time_keys.h
index 2854131..5b948f1 100644
--- a/media/base/watch_time_keys.h
+++ b/media/base/watch_time_keys.h
@@ -21,6 +21,8 @@
 MEDIA_EXPORT extern const char kWatchTimeAudioBattery[];
 MEDIA_EXPORT extern const char kWatchTimeAudioAc[];
 MEDIA_EXPORT extern const char kWatchTimeAudioEmbeddedExperience[];
+MEDIA_EXPORT extern const char kWatchTimeAudioNativeControlsOn[];
+MEDIA_EXPORT extern const char kWatchTimeAudioNativeControlsOff[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoAll[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoMse[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoEme[];
@@ -28,6 +30,8 @@
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoBattery[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoAc[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoEmbeddedExperience[];
+MEDIA_EXPORT extern const char kWatchTimeAudioVideoNativeControlsOn[];
+MEDIA_EXPORT extern const char kWatchTimeAudioVideoNativeControlsOff[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundAll[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundMse[];
 MEDIA_EXPORT extern const char kWatchTimeAudioVideoBackgroundEme[];
@@ -41,6 +45,7 @@
 // Markers which signify the watch time should be finalized immediately.
 MEDIA_EXPORT extern const char kWatchTimeFinalize[];
 MEDIA_EXPORT extern const char kWatchTimeFinalizePower[];
+MEDIA_EXPORT extern const char kWatchTimeFinalizeControls[];
 
 // Count of the number of underflow events during a media session.
 MEDIA_EXPORT extern const char kWatchTimeUnderflowCount[];
@@ -55,6 +60,7 @@
 
 MEDIA_EXPORT base::flat_set<base::StringPiece> GetWatchTimeKeys();
 MEDIA_EXPORT base::flat_set<base::StringPiece> GetWatchTimePowerKeys();
+MEDIA_EXPORT base::flat_set<base::StringPiece> GetWatchTimeControlsKeys();
 
 }  // namespace media
 
diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc
index 7bd76741..8d249327 100644
--- a/media/blink/watch_time_reporter.cc
+++ b/media/blink/watch_time_reporter.cc
@@ -178,6 +178,38 @@
   pending_underflow_events_.push_back(get_media_time_cb_.Run());
 }
 
+void WatchTimeReporter::OnNativeControlsEnabled() {
+  if (!reporting_timer_.IsRunning()) {
+    has_native_controls_ = true;
+    return;
+  }
+
+  if (end_timestamp_for_controls_ != kNoTimestamp) {
+    end_timestamp_for_controls_ = kNoTimestamp;
+    return;
+  }
+
+  end_timestamp_for_controls_ = get_media_time_cb_.Run();
+  reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
+                         &WatchTimeReporter::UpdateWatchTime);
+}
+
+void WatchTimeReporter::OnNativeControlsDisabled() {
+  if (!reporting_timer_.IsRunning()) {
+    has_native_controls_ = false;
+    return;
+  }
+
+  if (end_timestamp_for_controls_ != kNoTimestamp) {
+    end_timestamp_for_controls_ = kNoTimestamp;
+    return;
+  }
+
+  end_timestamp_for_controls_ = get_media_time_cb_.Run();
+  reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
+                         &WatchTimeReporter::UpdateWatchTime);
+}
+
 void WatchTimeReporter::OnPowerStateChange(bool on_battery_power) {
   if (!reporting_timer_.IsRunning())
     return;
@@ -229,9 +261,10 @@
 
   underflow_count_ = 0;
   last_media_timestamp_ = last_media_power_timestamp_ =
-      end_timestamp_for_power_ = kNoTimestamp;
+      last_media_controls_timestamp_ = end_timestamp_for_power_ = kNoTimestamp;
   is_on_battery_power_ = IsOnBatteryPower();
-  start_timestamp_ = start_timestamp_for_power_ = start_timestamp;
+  start_timestamp_ = start_timestamp_for_power_ =
+      start_timestamp_for_controls_ = start_timestamp;
   reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
                          &WatchTimeReporter::UpdateWatchTime);
 }
@@ -262,6 +295,8 @@
 
   const bool is_finalizing = end_timestamp_ != kNoTimestamp;
   const bool is_power_change_pending = end_timestamp_for_power_ != kNoTimestamp;
+  const bool is_controls_change_pending =
+      end_timestamp_for_controls_ != kNoTimestamp;
 
   // If we're finalizing the log, use the media time value at the time of
   // finalization.
@@ -281,6 +316,15 @@
         value.InSecondsF());                                               \
   } while (0)
 
+#define RECORD_CONTROLS_WATCH_TIME(key, value)                         \
+  do {                                                                 \
+    if (is_background_)                                                \
+      break;                                                           \
+    log_event->params.SetDoubleWithoutPathExpansion(                   \
+        has_video_ ? kWatchTimeAudioVideo##key : kWatchTimeAudio##key, \
+        value.InSecondsF());                                           \
+  } while (0)
+
   // Only report watch time after some minimum amount has elapsed. Don't update
   // watch time if media time hasn't changed since the last run; this may occur
   // if a seek is taking some time to complete or the playback is stalled for
@@ -324,7 +368,25 @@
         RECORD_WATCH_TIME(Ac, elapsed_power);
     }
   }
+
+  // Similar to the block above for controls.
+  if (last_media_controls_timestamp_ != current_timestamp) {
+    last_media_controls_timestamp_ = is_controls_change_pending
+                                         ? end_timestamp_for_controls_
+                                         : current_timestamp;
+
+    const base::TimeDelta elapsed_controls =
+        last_media_controls_timestamp_ - start_timestamp_for_controls_;
+
+    if (elapsed_controls >= kMinimumElapsedWatchTime) {
+      if (has_native_controls_)
+        RECORD_CONTROLS_WATCH_TIME(NativeControlsOn, elapsed_controls);
+      else
+        RECORD_CONTROLS_WATCH_TIME(NativeControlsOff, elapsed_controls);
+    }
+  }
 #undef RECORD_WATCH_TIME
+#undef RECORD_CONTROLS_WATCH_TIME
 
   // Pass along any underflow events which have occurred since the last report.
   if (!pending_underflow_events_.empty()) {
@@ -345,10 +407,14 @@
 
   // Always send finalize, even if we don't currently have any data, it's
   // harmless to send since nothing will be logged if we've already finalized.
-  if (is_finalizing)
+  if (is_finalizing) {
     log_event->params.SetBoolean(kWatchTimeFinalize, true);
-  else if (is_power_change_pending)
-    log_event->params.SetBoolean(kWatchTimeFinalizePower, true);
+  } else {
+    if (is_power_change_pending)
+      log_event->params.SetBoolean(kWatchTimeFinalizePower, true);
+    if (is_controls_change_pending)
+      log_event->params.SetBoolean(kWatchTimeFinalizeControls, true);
+  }
 
   if (!log_event->params.empty())
     media_log_->AddEvent(std::move(log_event));
@@ -362,6 +428,13 @@
     end_timestamp_for_power_ = kNoTimestamp;
   }
 
+  if (is_controls_change_pending) {
+    has_native_controls_ = !has_native_controls_;
+
+    start_timestamp_for_controls_ = end_timestamp_for_controls_;
+    end_timestamp_for_controls_ = kNoTimestamp;
+  }
+
   // Stop the timer if this is supposed to be our last tick.
   if (is_finalizing) {
     end_timestamp_ = kNoTimestamp;
diff --git a/media/blink/watch_time_reporter.h b/media/blink/watch_time_reporter.h
index 0ff48fd8..51cfa78d 100644
--- a/media/blink/watch_time_reporter.h
+++ b/media/blink/watch_time_reporter.h
@@ -114,6 +114,11 @@
   // number of rebuffering events. Reset to zero after a finalize event.
   void OnUnderflow();
 
+  // These methods are used to ensure that the watch time is reported relative
+  // to whether the media is using native controls.
+  void OnNativeControlsEnabled();
+  void OnNativeControlsDisabled();
+
   // Setup the reporting interval to be immediate to avoid spinning real time
   // within the unit test.
   void set_reporting_interval_for_testing() {
@@ -177,6 +182,7 @@
   bool is_on_battery_power_ = false;
   bool is_playing_ = false;
   bool is_visible_ = true;
+  bool has_native_controls_ = false;
   double volume_ = 1.0;
   int underflow_count_ = 0;
   std::vector<base::TimeDelta> pending_underflow_events_;
@@ -184,6 +190,7 @@
   // The last media timestamp seen by UpdateWatchTime().
   base::TimeDelta last_media_timestamp_ = kNoTimestamp;
   base::TimeDelta last_media_power_timestamp_ = kNoTimestamp;
+  base::TimeDelta last_media_controls_timestamp_ = kNoTimestamp;
 
   // The starting and ending timestamps used for reporting watch time.
   base::TimeDelta start_timestamp_;
@@ -194,6 +201,11 @@
   base::TimeDelta start_timestamp_for_power_;
   base::TimeDelta end_timestamp_for_power_ = kNoTimestamp;
 
+  // Similar to the above but tracks watch time relative to whether or not
+  // native controls are being used.
+  base::TimeDelta start_timestamp_for_controls_;
+  base::TimeDelta end_timestamp_for_controls_ = kNoTimestamp;
+
   // Special case reporter for handling background video watch time. Configured
   // as an audio only WatchTimeReporter with |is_background_| set to true.
   std::unique_ptr<WatchTimeReporter> background_reporter_;
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index 5a65d106..18ddf020 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -41,6 +41,9 @@
 #define EXPECT_POWER_WATCH_TIME_FINALIZED() \
   EXPECT_CALL(media_log_, OnPowerWatchTimeFinalized()).RetiresOnSaturation();
 
+#define EXPECT_CONTROLS_WATCH_TIME_FINALIZED() \
+  EXPECT_CALL(media_log_, OnControlsWatchTimeFinalized()).RetiresOnSaturation();
+
 class WatchTimeReporterTest : public testing::TestWithParam<bool> {
  public:
   WatchTimeReporterTest() : has_video_(GetParam()) {}
@@ -49,7 +52,7 @@
  protected:
   class WatchTimeLogMonitor : public MediaLog {
    public:
-    WatchTimeLogMonitor() {}
+    WatchTimeLogMonitor() = default;
 
     void AddEvent(std::unique_ptr<MediaLogEvent> event) override {
       ASSERT_EQ(event->type, MediaLogEvent::Type::WATCH_TIME_UPDATE);
@@ -60,8 +63,12 @@
         if (it.value().GetAsBoolean(&finalize)) {
           if (it.key() == kWatchTimeFinalize)
             OnWatchTimeFinalized();
-          else
+          else if (it.key() == kWatchTimeFinalizePower)
             OnPowerWatchTimeFinalized();
+          else if (it.key() == kWatchTimeFinalizeControls)
+            OnControlsWatchTimeFinalized();
+          else
+            NOTREACHED();
           continue;
         }
 
@@ -79,11 +86,12 @@
 
     MOCK_METHOD0(OnWatchTimeFinalized, void(void));
     MOCK_METHOD0(OnPowerWatchTimeFinalized, void(void));
+    MOCK_METHOD0(OnControlsWatchTimeFinalized, void(void));
     MOCK_METHOD2(OnWatchTimeUpdate, void(const std::string&, base::TimeDelta));
     MOCK_METHOD1(OnUnderflowUpdate, void(int));
 
    protected:
-    ~WatchTimeLogMonitor() override {}
+    ~WatchTimeLogMonitor() override = default;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(WatchTimeLogMonitor);
@@ -134,6 +142,11 @@
       wtr_->background_reporter_->OnPowerStateChange(on_battery_power);
   }
 
+  void OnNativeControlsEnabled(bool enabled) {
+    enabled ? wtr_->OnNativeControlsEnabled()
+            : wtr_->OnNativeControlsDisabled();
+  }
+
   enum {
     // After |test_callback_func| is executed, should watch time continue to
     // accumulate?
@@ -148,9 +161,9 @@
     // used with |kStartOnBattery| it will be the battery metric.
     kFinalizePowerWatchTime = 4,
 
-    // During finalize the watch time should continue on the metric opposite the
-    // starting metric (by default it's AC, it's battery if |kStartOnBattery| is
-    // specified.
+    // During finalize the power watch time should continue on the metric
+    // opposite the starting metric (by default it's AC, it's battery if
+    // |kStartOnBattery| is specified.
     kTransitionPowerWatchTime = 8,
 
     // Indicates that power watch time should be reported to the battery metric.
@@ -158,6 +171,21 @@
 
     // Indicates an extra start event may be generated during test execution.
     kFinalizeInterleavedStartEvent = 32,
+
+    // During finalize the watch time should not continue on the starting
+    // controls metric. By default this means the NativeControsOff metric will
+    // be finalized, but if used with |kStartWithNativeControls| it will be the
+    // NativeControlsOn metric.
+    kFinalizeControlsWatchTime = 64,
+
+    // During finalize the controls watch time should continue on the metric
+    // opposite the starting metric (by default it's non-native controls, it's
+    // native controls if |kStartWithNativeControls| is specified.
+    kTransitionControlsWatchTime = 128,
+
+    // Indicates that controls watch time should be reported to the native
+    // controls metric.
+    kStartWithNativeControls = 256,
   };
 
   template <int TestFlags = 0, typename HysteresisTestCallback>
@@ -167,6 +195,9 @@
     // Disable background reporting for the hysteresis tests.
     wtr_->background_reporter_.reset();
 
+    if (TestFlags & kStartWithNativeControls)
+      OnNativeControlsEnabled(true);
+
     // Setup all current time expectations first since they need to use the
     // InSequence macro for ease of use, but we don't want the watch time
     // expectations to be in sequence (or expectations would depend on sorted
@@ -177,6 +208,7 @@
     constexpr base::TimeDelta kWatchTime4 = base::TimeDelta::FromSeconds(30);
     {
       testing::InSequence s;
+
       EXPECT_CALL(*this, GetCurrentMediaTime())
           .WillOnce(testing::Return(base::TimeDelta()))
           .WillOnce(testing::Return(kWatchTime1));
@@ -187,7 +219,8 @@
       if (TestFlags & kAccumulationContinuesAfterTest) {
         EXPECT_CALL(*this, GetCurrentMediaTime())
             .Times(TestFlags & (kFinalizeExitDoesNotRequireCurrentTime |
-                                kFinalizePowerWatchTime)
+                                kFinalizePowerWatchTime |
+                                kFinalizeControlsWatchTime)
                        ? 1
                        : 2)
             .WillRepeatedly(testing::Return(kWatchTime2));
@@ -204,6 +237,11 @@
         EXPECT_CALL(*this, GetCurrentMediaTime())
             .WillOnce(testing::Return(kWatchTime4));
       }
+
+      if (TestFlags & kTransitionControlsWatchTime) {
+        EXPECT_CALL(*this, GetCurrentMediaTime())
+            .WillOnce(testing::Return(kWatchTime4));
+      }
     }
 
     wtr_->OnPlaying();
@@ -219,6 +257,10 @@
       EXPECT_WATCH_TIME(Battery, kWatchTime1);
     else
       EXPECT_WATCH_TIME(Ac, kWatchTime1);
+    if (TestFlags & kStartWithNativeControls)
+      EXPECT_WATCH_TIME(NativeControlsOn, kWatchTime1);
+    else
+      EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime1);
 
     CycleReportingTimer();
 
@@ -232,34 +274,59 @@
     EXPECT_WATCH_TIME(Src, kExpectedWatchTime);
     const base::TimeDelta kExpectedPowerWatchTime =
         TestFlags & kFinalizePowerWatchTime ? kWatchTime2 : kExpectedWatchTime;
+    const base::TimeDelta kExpectedContolsWatchTime =
+        TestFlags & kFinalizeControlsWatchTime ? kWatchTime2
+                                               : kExpectedWatchTime;
+
     if (TestFlags & kStartOnBattery)
       EXPECT_WATCH_TIME(Battery, kExpectedPowerWatchTime);
     else
       EXPECT_WATCH_TIME(Ac, kExpectedPowerWatchTime);
 
-    // If we're not testing battery watch time, this is the end of the test.
-    if (!(TestFlags & kTransitionPowerWatchTime)) {
-      EXPECT_WATCH_TIME_FINALIZED();
-      wtr_.reset();
-      return;
+    if (TestFlags & kStartWithNativeControls)
+      EXPECT_WATCH_TIME(NativeControlsOn, kExpectedContolsWatchTime);
+    else
+      EXPECT_WATCH_TIME(NativeControlsOff, kExpectedContolsWatchTime);
+
+    // Special case when testing battery watch time.
+    if (TestFlags & kTransitionPowerWatchTime) {
+      ASSERT_TRUE(TestFlags & kAccumulationContinuesAfterTest)
+          << "kTransitionPowerWatchTime tests must be done with "
+             "kAccumulationContinuesAfterTest";
+
+      EXPECT_POWER_WATCH_TIME_FINALIZED();
+      CycleReportingTimer();
+
+      // Run one last cycle that is long enough to trigger a new watch time
+      // entry on the opposite of the current power watch time graph; i.e. if we
+      // started on battery we'll now record one for ac and vice versa.
+      EXPECT_WATCH_TIME(All, kWatchTime4);
+      EXPECT_WATCH_TIME(Src, kWatchTime4);
+      EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime4);
+      if (TestFlags & kStartOnBattery)
+        EXPECT_WATCH_TIME(Ac, kWatchTime4 - kWatchTime2);
+      else
+        EXPECT_WATCH_TIME(Battery, kWatchTime4 - kWatchTime2);
+    } else if (TestFlags & kTransitionControlsWatchTime) {
+      ASSERT_TRUE(TestFlags & kAccumulationContinuesAfterTest)
+          << "kTransitionControlsWatchTime tests must be done with "
+             "kAccumulationContinuesAfterTest";
+
+      EXPECT_CONTROLS_WATCH_TIME_FINALIZED();
+      CycleReportingTimer();
+
+      // Run one last cycle that is long enough to trigger a new watch time
+      // entry on the opposite of the current power watch time graph; i.e. if we
+      // started on battery we'll now record one for ac and vice versa.
+      EXPECT_WATCH_TIME(All, kWatchTime4);
+      EXPECT_WATCH_TIME(Src, kWatchTime4);
+      EXPECT_WATCH_TIME(Ac, kWatchTime4);
+      if (TestFlags & kStartWithNativeControls)
+        EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime4 - kWatchTime2);
+      else
+        EXPECT_WATCH_TIME(NativeControlsOn, kWatchTime4 - kWatchTime2);
     }
 
-    ASSERT_TRUE(TestFlags & kAccumulationContinuesAfterTest)
-        << "kTransitionPowerWatchTime tests must be done with "
-           "kAccumulationContinuesAfterTest";
-
-    EXPECT_POWER_WATCH_TIME_FINALIZED();
-    CycleReportingTimer();
-
-    // Run one last cycle that is long enough to trigger a new watch time entry
-    // on the opposite of the current power watch time graph; i.e. if we started
-    // on battery we'll now record one for ac and vice versa.
-    EXPECT_WATCH_TIME(All, kWatchTime4);
-    EXPECT_WATCH_TIME(Src, kWatchTime4);
-    if (TestFlags & kStartOnBattery)
-      EXPECT_WATCH_TIME(Ac, kWatchTime4 - kWatchTime2);
-    else
-      EXPECT_WATCH_TIME(Battery, kWatchTime4 - kWatchTime2);
     EXPECT_WATCH_TIME_FINALIZED();
     wtr_.reset();
   }
@@ -352,6 +419,7 @@
   EXPECT_WATCH_TIME(All, kWatchTimeLate);
   EXPECT_WATCH_TIME(Eme, kWatchTimeLate);
   EXPECT_WATCH_TIME(Mse, kWatchTimeLate);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeLate);
   EXPECT_CALL(media_log_, OnUnderflowUpdate(2));
   CycleReportingTimer();
 
@@ -389,6 +457,7 @@
   EXPECT_WATCH_TIME(All, kWatchTimeEarly);
   EXPECT_WATCH_TIME(Eme, kWatchTimeEarly);
   EXPECT_WATCH_TIME(Mse, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeEarly);
   EXPECT_CALL(media_log_, OnUnderflowUpdate(1));
   EXPECT_WATCH_TIME_FINALIZED();
   CycleReportingTimer();
@@ -428,6 +497,7 @@
   EXPECT_WATCH_TIME(All, kExpectedWatchTime);
   EXPECT_WATCH_TIME(Eme, kExpectedWatchTime);
   EXPECT_WATCH_TIME(Mse, kExpectedWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kExpectedWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 }
@@ -489,6 +559,7 @@
       .WillOnce(testing::Return(kWatchTimeEarly))
       .WillOnce(testing::Return(kWatchTimeEarly))
       .WillRepeatedly(testing::Return(kWatchTimeLate));
+
   Initialize(true, true, true, kSizeJustRight);
   wtr_->OnHidden();
   wtr_->OnPlaying();
@@ -607,6 +678,87 @@
   wtr_.reset();
 }
 
+TEST_P(WatchTimeReporterTest, WatchTimeReporterHiddenControlsBackground) {
+  // Only run these background tests when video is present.
+  if (!has_video_)
+    return;
+
+  constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(8);
+  constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(16);
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime2));
+  Initialize(true, true, true, kSizeJustRight);
+  wtr_->OnHidden();
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsBackgroundMonitoring());
+  EXPECT_FALSE(IsMonitoring());
+
+  OnNativeControlsEnabled(true);
+
+  EXPECT_BACKGROUND_WATCH_TIME(Ac, kWatchTime1);
+  EXPECT_BACKGROUND_WATCH_TIME(All, kWatchTime1);
+  EXPECT_BACKGROUND_WATCH_TIME(Eme, kWatchTime1);
+  EXPECT_BACKGROUND_WATCH_TIME(Mse, kWatchTime1);
+  CycleReportingTimer();
+
+  wtr_->OnPaused();
+  EXPECT_BACKGROUND_WATCH_TIME(Ac, kWatchTime2);
+  EXPECT_BACKGROUND_WATCH_TIME(All, kWatchTime2);
+  EXPECT_BACKGROUND_WATCH_TIME(Eme, kWatchTime2);
+  EXPECT_BACKGROUND_WATCH_TIME(Mse, kWatchTime2);
+  EXPECT_WATCH_TIME_FINALIZED();
+  CycleReportingTimer();
+
+  EXPECT_FALSE(IsBackgroundMonitoring());
+  EXPECT_FALSE(IsMonitoring());
+  wtr_.reset();
+}
+
+TEST_P(WatchTimeReporterTest, WatchTimeReporterControlsPowerFinalize) {
+  // Only run these background tests when video is present.
+  if (!has_video_)
+    return;
+
+  constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(8);
+  constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(16);
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillOnce(testing::Return(kWatchTime1))
+      .WillRepeatedly(testing::Return(kWatchTime2));
+  Initialize(true, true, true, kSizeJustRight);
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+
+  OnNativeControlsEnabled(true);
+  OnPowerStateChange(true);
+
+  EXPECT_WATCH_TIME(Ac, kWatchTime1);
+  EXPECT_WATCH_TIME(All, kWatchTime1);
+  EXPECT_WATCH_TIME(Eme, kWatchTime1);
+  EXPECT_WATCH_TIME(Mse, kWatchTime1);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime1);
+  EXPECT_CONTROLS_WATCH_TIME_FINALIZED();
+  EXPECT_POWER_WATCH_TIME_FINALIZED();
+  CycleReportingTimer();
+
+  wtr_->OnPaused();
+  EXPECT_WATCH_TIME(NativeControlsOn, kWatchTime2 - kWatchTime1);
+  EXPECT_WATCH_TIME(Battery, kWatchTime2 - kWatchTime1);
+  EXPECT_WATCH_TIME(All, kWatchTime2);
+  EXPECT_WATCH_TIME(Eme, kWatchTime2);
+  EXPECT_WATCH_TIME(Mse, kWatchTime2);
+  EXPECT_WATCH_TIME_FINALIZED();
+  CycleReportingTimer();
+
+  EXPECT_FALSE(IsMonitoring());
+  wtr_.reset();
+}
+
 // Tests that starting from a non-zero base works.
 TEST_P(WatchTimeReporterTest, WatchTimeReporterNonZeroStart) {
   constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(5);
@@ -623,6 +775,7 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Eme, kWatchTime);
   EXPECT_WATCH_TIME(Mse, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   CycleReportingTimer();
 
   EXPECT_WATCH_TIME_FINALIZED();
@@ -643,6 +796,7 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Eme, kWatchTime);
   EXPECT_WATCH_TIME(Mse, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_->OnSeeking();
 }
@@ -662,6 +816,7 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Eme, kWatchTime);
   EXPECT_WATCH_TIME(Mse, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_->OnPaused();
   wtr_->OnSeeking();
@@ -682,6 +837,7 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Eme, kWatchTime);
   EXPECT_WATCH_TIME(Mse, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 }
@@ -690,7 +846,7 @@
 TEST_P(WatchTimeReporterTest, WatchTimeCategoryMapping) {
   constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(10);
 
-  // Verify ac, all, src
+  // Verify ac, all, src, non-native controls
   EXPECT_CALL(*this, GetCurrentMediaTime())
       .WillOnce(testing::Return(base::TimeDelta()))
       .WillOnce(testing::Return(kWatchTime));
@@ -700,10 +856,11 @@
   EXPECT_WATCH_TIME(Ac, kWatchTime);
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Src, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 
-  // Verify ac, all, mse
+  // Verify ac, all, mse, non-native controls
   EXPECT_CALL(*this, GetCurrentMediaTime())
       .WillOnce(testing::Return(base::TimeDelta()))
       .WillOnce(testing::Return(kWatchTime));
@@ -713,10 +870,11 @@
   EXPECT_WATCH_TIME(Ac, kWatchTime);
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Mse, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 
-  // Verify ac, all, eme, src
+  // Verify ac, all, eme, src, non-native controls
   EXPECT_CALL(*this, GetCurrentMediaTime())
       .WillOnce(testing::Return(base::TimeDelta()))
       .WillOnce(testing::Return(kWatchTime));
@@ -727,10 +885,11 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Eme, kWatchTime);
   EXPECT_WATCH_TIME(Src, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 
-  // Verify all, battery, src
+  // Verify all, battery, src, non-native controls
   EXPECT_CALL(*this, GetCurrentMediaTime())
       .WillOnce(testing::Return(base::TimeDelta()))
       .WillOnce(testing::Return(kWatchTime));
@@ -741,6 +900,22 @@
   EXPECT_WATCH_TIME(All, kWatchTime);
   EXPECT_WATCH_TIME(Battery, kWatchTime);
   EXPECT_WATCH_TIME(Src, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTime);
+  EXPECT_WATCH_TIME_FINALIZED();
+  wtr_.reset();
+
+  // Verify ac, all, src, native controls
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTime));
+  Initialize(true, false, false, kSizeJustRight);
+  OnNativeControlsEnabled(true);
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+  EXPECT_WATCH_TIME(Ac, kWatchTime);
+  EXPECT_WATCH_TIME(All, kWatchTime);
+  EXPECT_WATCH_TIME(Src, kWatchTime);
+  EXPECT_WATCH_TIME(NativeControlsOn, kWatchTime);
   EXPECT_WATCH_TIME_FINALIZED();
   wtr_.reset();
 }
@@ -821,6 +996,48 @@
       [this]() { OnPowerStateChange(true); });
 }
 
+TEST_P(WatchTimeReporterTest, OnControlsChangeHysteresisNativeContinuation) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeExitDoesNotRequireCurrentTime |
+                    kStartWithNativeControls>([this]() {
+    OnNativeControlsEnabled(false);
+    OnNativeControlsEnabled(true);
+  });
+}
+
+TEST_P(WatchTimeReporterTest, OnControlsChangeHysteresisNativeFinalized) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeControlsWatchTime | kStartWithNativeControls>(
+      [this]() { OnNativeControlsEnabled(false); });
+}
+
+TEST_P(WatchTimeReporterTest, OnControlsChangeHysteresisNativeOffContinuation) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeExitDoesNotRequireCurrentTime>([this]() {
+    OnNativeControlsEnabled(true);
+    OnNativeControlsEnabled(false);
+  });
+}
+
+TEST_P(WatchTimeReporterTest, OnControlsChangeHysteresisNativeOffFinalized) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeControlsWatchTime>(
+      [this]() { OnNativeControlsEnabled(true); });
+}
+
+TEST_P(WatchTimeReporterTest, OnControlsChangeToNativeOff) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeControlsWatchTime | kStartWithNativeControls |
+                    kTransitionControlsWatchTime>(
+      [this]() { OnNativeControlsEnabled(false); });
+}
+
+TEST_P(WatchTimeReporterTest, OnControlsChangeToNative) {
+  RunHysteresisTest<kAccumulationContinuesAfterTest |
+                    kFinalizeControlsWatchTime | kTransitionControlsWatchTime>(
+      [this]() { OnNativeControlsEnabled(true); });
+}
+
 // Tests that the first finalize is the only one that matters.
 TEST_P(WatchTimeReporterTest, HysteresisFinalizedWithEarliest) {
   RunHysteresisTest([this]() {
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index f30404f..7f21f07 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -433,6 +433,16 @@
   delegate_->SetIsEffectivelyFullscreen(delegate_id_, isEffectivelyFullscreen);
 }
 
+void WebMediaPlayerImpl::OnHasNativeControlsChanged(bool has_native_controls) {
+  if (!watch_time_reporter_)
+    return;
+
+  if (has_native_controls)
+    watch_time_reporter_->OnNativeControlsEnabled();
+  else
+    watch_time_reporter_->OnNativeControlsDisabled();
+}
+
 void WebMediaPlayerImpl::DoLoad(LoadType load_type,
                                 const blink::WebURL& url,
                                 CORSMode cors_mode) {
@@ -2295,6 +2305,10 @@
     watch_time_reporter_->OnHidden();
   else
     watch_time_reporter_->OnShown();
+  if (client_->HasNativeControls())
+    watch_time_reporter_->OnNativeControlsEnabled();
+  else
+    watch_time_reporter_->OnNativeControlsDisabled();
 }
 
 bool WebMediaPlayerImpl::IsHidden() const {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 8a6c51b..df65ecb 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -193,6 +193,7 @@
   void ExitedFullscreen() override;
   void BecameDominantVisibleContent(bool isDominant) override;
   void SetIsEffectivelyFullscreen(bool isEffectivelyFullscreen) override;
+  void OnHasNativeControlsChanged(bool) override;
 
   // WebMediaPlayerDelegate::Observer implementation.
   void OnFrameHidden() override;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 160049e..efbeaf22 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -102,6 +102,7 @@
   blink::WebMediaPlayer::TrackId GetSelectedVideoTrackId() override {
     return blink::WebMediaPlayer::TrackId();
   }
+  bool HasNativeControls() override { return false; }
 
   void set_is_autoplaying_muted(bool value) { is_autoplaying_muted_ = value; }
 
diff --git a/media/cast/net/pacing/paced_sender.h b/media/cast/net/pacing/paced_sender.h
index 23b6c09..9dc7e34 100644
--- a/media/cast/net/pacing/paced_sender.h
+++ b/media/cast/net/pacing/paced_sender.h
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 721f7b8..4dbeba25 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -4770,42 +4770,18 @@
   EXPECT_EQ(nullptr, GetStream(DemuxerStream::VIDEO));
 }
 
-// TODO(servolk): Add a unit test with multiple audio/video tracks using the
-// same codec type in a single SourceBufferState, when WebM parser supports
-// multiple tracks. crbug.com/646900
-
-class ChunkDemuxerMp4Vp9Test : public ChunkDemuxerTest,
-                               public WithParamInterface<bool> {
- public:
-  void SetUp() override {
-    ChunkDemuxerTest::SetUp();
-    const bool enable_mp4_vp9_demuxing = GetParam();
-    if (enable_mp4_vp9_demuxing) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kEnableVp9InMp4);
-    }
-  }
-};
-
-TEST_P(ChunkDemuxerMp4Vp9Test, CodecSupport) {
+TEST_F(ChunkDemuxerTest, Mp4Vp9CodecSupport) {
   ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
-
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  const bool enable_mp4_vp9_demuxing = GetParam();
-  if (enable_mp4_vp9_demuxing) {
-    expected = ChunkDemuxer::kOk;
-  } else {
-    EXPECT_MEDIA_LOG(
-        HasSubstr("Codec 'vp09.00.10.08' is not supported for 'video/mp4'"));
-  }
+  expected = ChunkDemuxer::kOk;
 #endif
 
   EXPECT_EQ(demuxer_->AddId("source_id", "video/mp4", "vp09.00.10.08"),
             expected);
 }
 
-INSTANTIATE_TEST_CASE_P(EnableDisableMp4Vp9Demuxing,
-                        ChunkDemuxerMp4Vp9Test,
-                        ::testing::Bool());
+// TODO(servolk): Add a unit test with multiple audio/video tracks using the
+// same codec type in a single SourceBufferState, when WebM parser supports
+// multiple tracks. crbug.com/646900
 
 }  // namespace media
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc
index 55e03b7..d76885be 100644
--- a/media/filters/stream_parser_factory.cc
+++ b/media/filters/stream_parser_factory.cc
@@ -107,11 +107,6 @@
 }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-bool CheckIfMp4Vp9DemuxingEnabled(const std::string& codec_id,
-                                  MediaLog* media_log) {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableVp9InMp4);
-}
 
 // AAC Object Type IDs that Chrome supports.
 static const int kAACLCObjectType = 2;
@@ -178,8 +173,7 @@
     "dvhe.*", CodecInfo::VIDEO, NULL, CodecInfo::HISTOGRAM_DOLBYVISION};
 #endif
 #endif
-static const CodecInfo kMPEG4VP09CodecInfo = {"vp09.*", CodecInfo::VIDEO,
-                                              &CheckIfMp4Vp9DemuxingEnabled,
+static const CodecInfo kMPEG4VP09CodecInfo = {"vp09.*", CodecInfo::VIDEO, NULL,
                                               CodecInfo::HISTOGRAM_VP9};
 static const CodecInfo kMPEG4AACCodecInfo = { "mp4a.40.*", CodecInfo::AUDIO,
                                               &ValidateMP4ACodecID,
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index 329d74e..7e581bc 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -804,21 +804,16 @@
     }
 #endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
-    case FOURCC_VP09:
-      if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kEnableVp9InMp4)) {
-        DVLOG(2) << __func__ << " parsing VPCodecConfigurationRecord (vpcC)";
-        std::unique_ptr<VPCodecConfigurationRecord> vp_config(
-            new VPCodecConfigurationRecord());
-        RCHECK(reader->ReadChild(vp_config.get()));
-        frame_bitstream_converter = nullptr;
-        video_codec = kCodecVP9;
-        video_codec_profile = vp_config->profile;
-      } else {
-        MEDIA_LOG(ERROR, reader->media_log()) << "VP9 in MP4 is not enabled.";
-        return false;
-      }
+    case FOURCC_VP09: {
+      DVLOG(2) << __func__ << " parsing VPCodecConfigurationRecord (vpcC)";
+      std::unique_ptr<VPCodecConfigurationRecord> vp_config(
+          new VPCodecConfigurationRecord());
+      RCHECK(reader->ReadChild(vp_config.get()));
+      frame_bitstream_converter = nullptr;
+      video_codec = kCodecVP9;
+      video_codec_profile = vp_config->profile;
       break;
+    }
     default:
       // Unknown/unsupported format
       MEDIA_LOG(ERROR, reader->media_log()) << __func__
@@ -848,10 +843,8 @@
     case FOURCC_DVA1:
     case FOURCC_DVAV:
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
-      return true;
     case FOURCC_VP09:
-      return base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableVp9InMp4);
+      return true;
     default:
       return false;
   }
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h
index 0d31de58b..69aee2a 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -28,7 +28,6 @@
 #include "base/memory/linked_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread.h"
 #include "base/win/scoped_comptr.h"
 #include "gpu/command_buffer/service/gpu_preferences.h"
diff --git a/media/gpu/jpeg_decode_accelerator_unittest.cc b/media/gpu/jpeg_decode_accelerator_unittest.cc
index 93c9934..888e748 100644
--- a/media/gpu/jpeg_decode_accelerator_unittest.cc
+++ b/media/gpu/jpeg_decode_accelerator_unittest.cc
@@ -392,11 +392,11 @@
     int width,
     int height,
     base::FilePath* filename) {
-  const int kBytesPerPixel = 3;
+  const int kBytesPerPixel = 4;
   const int kJpegQuality = 100;
   std::vector<unsigned char> input_buffer(width * height * kBytesPerPixel);
   std::vector<unsigned char> encoded;
-  if (!gfx::JPEGCodec::Encode(&input_buffer[0], gfx::JPEGCodec::FORMAT_RGB,
+  if (!gfx::JPEGCodec::Encode(&input_buffer[0], gfx::JPEGCodec::FORMAT_RGBA,
                               width, height, width * kBytesPerPixel,
                               kJpegQuality, &encoded)) {
     return false;
diff --git a/media/gpu/vaapi_jpeg_decode_accelerator.h b/media/gpu/vaapi_jpeg_decode_accelerator.h
index efa4e6391..4c00047 100644
--- a/media/gpu/vaapi_jpeg_decode_accelerator.h
+++ b/media/gpu/vaapi_jpeg_decode_accelerator.h
@@ -14,7 +14,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/gpu/media_gpu_export.h"
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 94cbf7b..0b9831e 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -1988,12 +1988,6 @@
        MAYBE_EME(EncryptedPlayback_MP4_VP9_CENC_VideoOnly)) {
   MockMediaSource source("bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVP9,
                          kAppendWholeFile);
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableVp9InMp4)) {
-    ASSERT_EQ(ChunkDemuxer::kNotSupported, source.AddId());
-    return;
-  }
-
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2028,12 +2022,6 @@
 TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_VP9) {
   MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
                          kAppendWholeFile);
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableVp9InMp4)) {
-    ASSERT_EQ(ChunkDemuxer::kNotSupported, source.AddId());
-    return;
-  }
-
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
   ASSERT_EQ(PIPELINE_OK, pipeline_status_);
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_proxy.h b/remoting/host/it2me/it2me_confirmation_dialog_proxy.h
index 42ca368..1fbf6c6 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_proxy.h
+++ b/remoting/host/it2me/it2me_confirmation_dialog_proxy.h
@@ -10,7 +10,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/non_thread_safe.h"
 #include "remoting/host/it2me/it2me_confirmation_dialog.h"
 
 namespace remoting {
diff --git a/remoting/host/local_input_monitor_chromeos.cc b/remoting/host/local_input_monitor_chromeos.cc
index c2ce05c..2ba0ec1 100644
--- a/remoting/host/local_input_monitor_chromeos.cc
+++ b/remoting/host/local_input_monitor_chromeos.cc
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/non_thread_safe.h"
 #include "remoting/host/chromeos/point_transformer.h"
 #include "remoting/host/client_session_control.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
diff --git a/remoting/protocol/transport.h b/remoting/protocol/transport.h
index 9b7720a..a3afebe 100644
--- a/remoting/protocol/transport.h
+++ b/remoting/protocol/transport.h
@@ -10,7 +10,6 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
 #include "net/base/ip_endpoint.h"
 #include "remoting/protocol/errors.h"
 
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc b/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
index 3df97a72..c212a70 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
@@ -216,4 +216,30 @@
   return kCPUUsageUnmeasuredForTesting;
 }
 
+base::Value CoordinationUnitImpl::GetProperty(mojom::PropertyType property) {
+  auto value_it = property_store_.find(property);
+
+  return value_it != property_store_.end() ? value_it->second : base::Value();
+}
+
+void CoordinationUnitImpl::ClearProperty(mojom::PropertyType property) {
+  property_store_.erase(property);
+}
+
+void CoordinationUnitImpl::SetProperty(mojom::PropertyPtr property) {
+  SetProperty(property->property, *property->value);
+}
+
+void CoordinationUnitImpl::SetProperty(mojom::PropertyType property,
+                                       base::Value value) {
+  // setting a property with an empty value is effectively clearing the
+  // value from storage
+  if (value.IsType(base::Value::Type::NONE)) {
+    ClearProperty(property);
+    return;
+  }
+
+  property_store_[property] = value;
+}
+
 }  // namespace resource_coordinator
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_impl.h b/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
index 69e0859..447b1af 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
@@ -8,9 +8,11 @@
 #include <list>
 #include <memory>
 #include <set>
+#include <unordered_map>
 #include <utility>
 
 #include "base/optional.h"
+#include "base/values.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -35,15 +37,27 @@
   void AddChild(const CoordinationUnitID& child_id) override;
   void SetCoordinationPolicyCallback(
       mojom::CoordinationPolicyCallbackPtr callback) override;
+  void SetProperty(mojom::PropertyPtr property) override;
 
   const CoordinationUnitID& id() const { return id_; }
   const std::set<CoordinationUnitImpl*>& children() const { return children_; }
   const std::set<CoordinationUnitImpl*>& parents() const { return parents_; }
+  const std::unordered_map<mojom::PropertyType, base::Value>&
+  property_store_for_testing() const {
+    return property_store_;
+  }
 
   static const double kCPUUsageMinimumForTesting;
   static const double kCPUUsageUnmeasuredForTesting;
   virtual double GetCPUUsageForTesting();
 
+  // Clear property from internal key-value store
+  void ClearProperty(mojom::PropertyType property);
+  // Retrieve property from internal key-value store
+  base::Value GetProperty(mojom::PropertyType property);
+  // Set property from internal key-value store
+  void SetProperty(mojom::PropertyType property, base::Value value);
+
  protected:
   const CoordinationUnitID id_;
   std::set<CoordinationUnitImpl*> children_;
@@ -60,6 +74,8 @@
   void RecalcCoordinationPolicy();
   void UnregisterCoordinationPolicyCallback();
 
+  std::unordered_map<mojom::PropertyType, base::Value> property_store_;
+
   enum StateFlags : uint8_t {
     kTestState,
     kTabVisible,
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_impl_unittest.cc b/services/resource_coordinator/coordination_unit/coordination_unit_impl_unittest.cc
index 143d0507..1b86c14 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_impl_unittest.cc
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_impl_unittest.cc
@@ -3,14 +3,19 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/values.h"
+#include "services/resource_coordinator/coordination_unit/coordination_unit_factory.h"
+#include "services/resource_coordinator/coordination_unit/coordination_unit_impl.h"
 #include "services/resource_coordinator/coordination_unit/coordination_unit_impl_unittest_util.h"
 #include "services/resource_coordinator/coordination_unit/coordination_unit_provider_impl.h"
+#include "services/resource_coordinator/public/interfaces/coordination_unit.mojom.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -172,4 +177,31 @@
   }
 }
 
+TEST_F(CoordinationUnitImplTest, GetSetProperty) {
+  CoordinationUnitID cu_id(CoordinationUnitType::kWebContents, std::string());
+
+  std::unique_ptr<CoordinationUnitImpl> coordination_unit =
+      coordination_unit_factory::CreateCoordinationUnit(
+          cu_id, service_context_ref_factory()->CreateRef());
+
+  // An empty value should be returned if property is not found
+  EXPECT_EQ(base::Value(),
+            coordination_unit->GetProperty(mojom::PropertyType::kTest));
+
+  // An empty value should be able to set a property
+  coordination_unit->SetProperty(mojom::PropertyType::kTest, base::Value());
+  EXPECT_EQ(0u, coordination_unit->property_store_for_testing().size());
+
+  base::Value int_val(41);
+
+  // Perform a valid storage property set
+  coordination_unit->SetProperty(mojom::PropertyType::kTest, int_val);
+  EXPECT_EQ(1u, coordination_unit->property_store_for_testing().size());
+  EXPECT_EQ(int_val,
+            coordination_unit->GetProperty(mojom::PropertyType::kTest));
+
+  coordination_unit->ClearProperty(mojom::PropertyType::kTest);
+  EXPECT_EQ(0u, coordination_unit->property_store_for_testing().size());
+}
+
 }  // namespace resource_coordinator
diff --git a/services/resource_coordinator/public/interfaces/coordination_unit.mojom b/services/resource_coordinator/public/interfaces/coordination_unit.mojom
index ce866b38f..f2c99d9 100644
--- a/services/resource_coordinator/public/interfaces/coordination_unit.mojom
+++ b/services/resource_coordinator/public/interfaces/coordination_unit.mojom
@@ -5,6 +5,7 @@
 module resource_coordinator.mojom;
 
 import "events.mojom";
+import "mojo/common/values.mojom";
 
 // Any new type here needs to be mirrored between coordination_unit_types.h and
 // coordination_unit.mojom, and have mappings between the two defined in
@@ -29,6 +30,19 @@
   SetCoordinationPolicy(CoordinationPolicy policy);
 };
 
+// Defines the Storage property keys that can be get/set on the
+// CoordinationUnitImpl internal key-value store.
+enum PropertyType {
+  kTest,
+};
+
+// Key-value pair that corresponds to an entry in the CoordinationUnitImpl's
+// internal key-value store
+struct Property {
+  PropertyType property;
+  mojo.common.mojom.Value value;
+};
+
 interface CoordinationUnit {
   SendEvent(Event event);
 
@@ -44,4 +58,7 @@
   // and can't be a direct ascendent or descendent of the current unit.
   AddChild(CoordinationUnitID child_id);
   SetCoordinationPolicyCallback(CoordinationPolicyCallback callback);
+
+  // Sets a property on the CoordinationUnitImpl's internal key-value store.
+  SetProperty(Property property);
 };
diff --git a/services/ui/gpu/gpu_service.h b/services/ui/gpu/gpu_service.h
index fc5d70c..0cf5d3c 100644
--- a/services/ui/gpu/gpu_service.h
+++ b/services/ui/gpu/gpu_service.h
@@ -9,7 +9,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/cancelable_task_tracker.h"
-#include "base/threading/non_thread_safe.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
diff --git a/skia/ext/image_operations.cc b/skia/ext/image_operations.cc
index 8b5f371d..5a3c2f49 100644
--- a/skia/ext/image_operations.cc
+++ b/skia/ext/image_operations.cc
@@ -345,16 +345,16 @@
   // Time how long this takes to see if it's a problem for users.
   base::TimeTicks resize_start = base::TimeTicks::Now();
 
-  SkIRect dest = { 0, 0, dest_width, dest_height };
-  DCHECK(dest.contains(dest_subset)) <<
-      "The supplied subset does not fall within the destination image.";
-
   // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
   // return empty.
   if (source.width() < 1 || source.height() < 1 ||
       dest_width < 1 || dest_height < 1)
     return SkBitmap();
 
+  SkIRect dest = {0, 0, dest_width, dest_height};
+  DCHECK(dest.contains(dest_subset))
+      << "The supplied subset does not fall within the destination image.";
+
   method = ResizeMethodToAlgorithmMethod(method);
   // Check that we deal with an "algorithm methods" from this point onward.
   SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
diff --git a/storage/browser/quota/usage_tracker.h b/storage/browser/quota/usage_tracker.h
index 343fab6..a4981217 100644
--- a/storage/browser/quota/usage_tracker.h
+++ b/storage/browser/quota/usage_tracker.h
@@ -14,7 +14,6 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
 #include "storage/browser/quota/quota_callbacks.h"
 #include "storage/browser/quota/quota_client.h"
 #include "storage/browser/quota/quota_task.h"
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index e5ee7fc..bd523f7 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -56,6 +56,7 @@
 -NavigationControllerBrowserTest.RefererStoredForSubFrame
 -ParallelDownloadTest.ParallelDownloadComplete
 -PaymentAppBrowserTest.PaymentAppInvocation
+-PaymentAppBrowserTest.PaymentAppOpenWindowFailed
 -PlzNavigateNavigationHandleImplBrowserTest.ErrorPageNetworkError
 -PreviewsStateResourceDispatcherHostBrowserTest.ShouldEnableLoFiModeNavigateBackThenForward
 -PreviewsStateResourceDispatcherHostBrowserTest.ShouldEnableLoFiModeOff
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8d0eed5e..c4b5446 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -491,21 +491,6 @@
             ]
         }
     ],
-    "ContextualSearch": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Default",
-                    "params": {
-                        "enable_ranker_logging": "true"
-                    }
-                }
-            ]
-        }
-    ],
     "CopylessPaste": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=MediaCastOverlayButton b/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=MediaCastOverlayButton
new file mode 100644
index 0000000..97a93b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=MediaCastOverlayButton
@@ -0,0 +1,6 @@
+# These tests should fail when the MediaCastOverlayButton is disabled.
+# See https://crbug.com/728660.
+
+Bug(none) media/controls/controls-overlay-cast-button.html [ Failure ]
+Bug(none) media/controls/video-overlay-cast-covering.html [ Failure ]
+Bug(none) media/controls/video-overlay-cast-light-rendering.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index ba48982..68c31ed 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -198,7 +198,7 @@
 Bug(none) external/wpt/css/css-shapes-1/shape-outside/values/shape-outside-ellipse-004.html [ Timeout ]
 Bug(none) external/wpt/css/CSS2/floats-clear/floats-015.xht [ Timeout ]
 Bug(none) external/wpt/cssom-view/scrolling-quirks-vs-nonquirks.html [ Timeout ]
-Bug(none) external/wpt/cssom-view/scrollingElement.html [ Failure ]
+Bug(none) external/wpt/cssom-view/scrollingElement.html [ Failure Timeout ]
 Bug(none) external/wpt/custom-elements/custom-element-registry/per-global.html [ Timeout ]
 Bug(none) external/wpt/dom/nodes/Document-characterSet-normalization.html [ Crash ]
 Bug(none) external/wpt/dom/nodes/Document-createElement-namespace.html [ Timeout ]
@@ -280,6 +280,7 @@
 Bug(none) external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-with-slow-text-tracks.html [ Timeout ]
 Bug(none) external/wpt/html/semantics/embedded-content/media-elements/video_008.htm [ Timeout ]
 Bug(none) external/wpt/html/semantics/embedded-content/media-elements/volume_nonfinite.html [ Timeout ]
+Bug(none) external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-load-event.html [ Timeout ]
 Bug(none) external/wpt/html/semantics/forms/form-submission-0/getactionurl.html [ Timeout ]
 Bug(none) external/wpt/html/semantics/forms/form-submission-0/submit-entity-body.html [ Timeout ]
 Bug(none) external/wpt/html/semantics/forms/the-legend-element/legend-form.html [ Timeout ]
@@ -291,6 +292,11 @@
 Bug(none) external/wpt/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html [ Timeout ]
 Bug(none) external/wpt/html/webappapis/the-windoworworkerglobalscope-mixin/Worker_Self_Origin.html [ Timeout ]
 Bug(none) external/wpt/IndexedDB [ Failure Timeout ]
+Bug(none) external/wpt/keyboard-lock/idlharness.https.html [ Timeout ]
+Bug(none) external/wpt/keyboard-lock/navigator-cancelKeyboardLock.https.html [ Timeout ]
+Bug(none) external/wpt/keyboard-lock/navigator-requestKeyboardLock-two-parallel-requests.https.html [ Timeout ]
+Bug(none) external/wpt/keyboard-lock/navigator-requestKeyboardLock-two-sequential-requests.https.html [ Timeout ]
+Bug(none) external/wpt/keyboard-lock/navigator-requestKeyboardLock.https.html [ Timeout ]
 Bug(none) external/wpt/media-source/mediasource-activesourcebuffers.html [ Timeout ]
 Bug(none) external/wpt/media-source/mediasource-append-buffer.html [ Timeout ]
 Bug(none) external/wpt/media-source/mediasource-appendwindow.html [ Timeout ]
@@ -1123,179 +1129,7 @@
 Bug(none) external/wpt/resource-timing/resource_TAO_multi.htm [ Failure ]
 Bug(none) external/wpt/resource-timing/test_resource_timing.html [ Failure ]
 Bug(none) external/wpt/secure-contexts/basic-popup-and-iframe-tests.html [ Failure ]
-Bug(none) external/wpt/service-workers/cache-storage/common.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-add.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-delete.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-matchAll.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-put.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-storage-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-storage-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/cache-storage.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/serviceworker/credentials.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-add.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-delete.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-matchAll.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-put.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-storage-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-storage-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/cache-storage.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/window/sandboxed-iframes.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-add.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-delete.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-matchAll.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-put.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-storage-keys.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-storage-match.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/cache-storage/worker/cache-storage.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/activate-event-after-install-state-change.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/activation-after-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/activation.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/active.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/appcache-ordering-main.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/claim-affect-other-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/claim-fetch.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/claim-not-using-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/claim-using-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/client-id.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-get-client-types.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-get.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-matchall-exact-controller.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-matchall-on-evaluation.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-matchall-order.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/clients-matchall.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/controller-on-disconnect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/controller-on-load.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/controller-on-reload.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/extendable-event-async-waituntil.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/extendable-event-waituntil.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-canvas-tainting.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-cors-xhr.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-csp.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-network-error.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-argument.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-readable-stream.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-response-body-with-invalid-chunk.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-throws-after-respond-with.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-within-sw-manual.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event-within-sw.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-event.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-css-base-url.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-css-cross-origin-mime-check.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-css-images.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-fallback.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-html-imports.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-no-freshness-headers.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-redirect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-request-resources.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-response-taint.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-response-xhr.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/fetch-waits-for-activate.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/foreign-fetch-basics.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/foreign-fetch-cors.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/foreign-fetch-event.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/foreign-fetch-workers.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/getregistration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/getregistrations.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/http-to-https-redirect-and-register.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/iframe-sandbox-register-link-element.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/import-scripts-resource-map.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/import-scripts-updated-flag.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/indexeddb.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/install-event-type.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/installing.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/interfaces.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/iso-latin1-header.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/multi-globals/url-parsing.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/multiple-register.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/multiple-update.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigate-window.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/chunked-encoding.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/get-state.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/redirect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/request-headers.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-preload/resource-timing.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-redirect-body.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-redirect-to-http.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/navigation-redirect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/onactivate-script-error.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/oninstall-script-error.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/opaque-response-preloaded.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/performance-timeline.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/postmessage-blob-url.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/postmessage-from-waiting-serviceworker.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/postmessage-msgport-to-client.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/postmessage-to-client.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/postmessage.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/redirected-response.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/referer.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/referrer-policy-header.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-closed-window.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-default-scope.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-foreign-fetch-errors.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-link-element.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-link-header.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-same-scope-different-script-url.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/register-wait-forever-in-install-worker.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/registration-end-to-end.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/registration-events.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/registration-iframe.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/registration-service-worker-attributes.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/rejections.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/request-body-blob.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/resource-timing.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/service-worker-csp-connect.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/service-worker-csp-default.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/service-worker-csp-script.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/serviceworker-message-event-historical.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event-constructor.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/service-worker-error-event.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/serviceworkerobject-scripturl.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/shared-worker-controlled.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/skip-waiting-installed.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/skip-waiting-using-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/skip-waiting-without-client.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/skip-waiting-without-using-registration.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/skip-waiting.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/state.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/synced-state.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/uncontrolled-page.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/unregister-controller.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/unregister-then-register-new-script.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/unregister-then-register.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/unregister.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/update-after-navigation-fetch-event.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/update-recovery.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/update.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/waiting.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/websocket.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/windowclient-navigate.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/worker-interception.https.html [ Timeout ]
-Bug(none) external/wpt/service-workers/service-worker/xhr.https.html [ Timeout ]
+Bug(none) external/wpt/service-workers [ Timeout ]
 Bug(none) external/wpt/storage/estimate-indexeddb-worker.https.html [ Timeout ]
 Bug(none) external/wpt/storage/estimate-indexeddb.https.html [ Timeout ]
 Bug(none) external/wpt/storage/interfaces.https.html [ Timeout ]
@@ -1833,6 +1667,7 @@
 Bug(none) http/tests/csspaint/paint-arguments.html [ Timeout ]
 Bug(none) http/tests/csspaint/paint-function-arguments.html [ Timeout ]
 Bug(none) http/tests/csspaint/paint2d-composite.html [ Timeout ]
+Bug(none) http/tests/csspaint/paint2d-filter.html [ Timeout ]
 Bug(none) http/tests/csspaint/paint2d-gradient.html [ Timeout ]
 Bug(none) http/tests/csspaint/paint2d-image.html [ Timeout ]
 Bug(none) http/tests/csspaint/paint2d-paths.html [ Timeout ]
@@ -2115,11 +1950,11 @@
 Bug(none) http/tests/https/verify-ssl-enabled.php [ Timeout ]
 Bug(none) http/tests/inspector-enabled [ Failure Timeout ]
 Bug(none) http/tests/inspector-protocol [ Failure Timeout ]
-Bug(none) http/tests/inspector [ Failure Timeout ]
+Bug(none) http/tests/inspector [ Crash Failure Timeout ]
 Bug(none) http/tests/linkHeader/link-preconnect-schemeless.https.php [ Timeout ]
 Bug(none) http/tests/loading/307-after-303-after-post.html [ Failure ]
 Bug(none) http/tests/loading/bad-scheme-subframe.html [ Failure ]
-Bug(none) http/tests/loading/pdf-commit-load-callbacks.html [ Timeout ]
+Bug(none) http/tests/loading/pdf-commit-load-callbacks.html [ Crash Timeout ]
 Bug(none) http/tests/loading/preload-image-sizes-2x.html [ Failure ]
 Bug(none) http/tests/loading/preload-picture-sizes-2x.html [ Failure ]
 Bug(none) http/tests/loading/redirect-with-no-location-crash.html [ Timeout ]
@@ -2505,7 +2340,7 @@
 Bug(none) http/tests/sendbeacon/beacon-cookie.html [ Failure ]
 Bug(none) http/tests/sendbeacon/beacon-cross-origin.https.html [ Timeout ]
 Bug(none) http/tests/sendbeacon/beacon-same-origin.html [ Failure ]
-Bug(none) http/tests/serviceworker [ Failure Timeout ]
+Bug(none) http/tests/serviceworker [ Crash Failure Timeout ]
 Bug(none) http/tests/storage/callbacks-are-called-in-correct-context.html [ Timeout ]
 Bug(none) http/tests/usb/secure-context.html [ Timeout ]
 Bug(none) http/tests/w3c/webperf/submission/Google/resource-timing/html/test_resource_redirects.html [ Failure ]
@@ -3362,7 +3197,7 @@
 Bug(none) usb/usbDevice.html [ Timeout ]
 Bug(none) vibration/vibration-iframe.html [ Timeout ]
 Bug(none) vibration/vibration.html [ Timeout ]
-Bug(none) virtual [ Failure Timeout ]
+Bug(none) virtual [ Crash Failure Timeout ]
 Bug(none) vr/events_vrdisplayactivate.html [ Timeout ]
 Bug(none) vr/events_vrdisplayconnect.html [ Timeout ]
 Bug(none) vr/events_vrdisplaypresentchange.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index d34592b0..6b6c99c 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -111,6 +111,7 @@
 Bug(none) virtual/new-remote-playback-pipeline/media/controls/video-controls-with-cast-rendering.html [ Failure ]
 Bug(none) virtual/new-remote-playback-pipeline/media/controls/video-overlay-cast-dark-rendering.html [ Failure ]
 Bug(none) virtual/new-remote-playback-pipeline/media/controls/video-overlay-cast-light-rendering.html [ Failure ]
+Bug(none) virtual/new-remote-playback-pipeline/media/controls/controls-cast-do-not-fade-out.html [ Failure Crash ]
 Bug(none) svg/foreignObject/overflow-clip-in-hidden-container-crash.html [ Crash ]
 Bug(none) images/rendering-broken-0px-images.html [ Failure ]
 Bug(none) images/rendering-broken-0px-images-quirk.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 8dea114..5b0f43d9 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -3002,3 +3002,5 @@
 crbug.com/729075 [ Mac10.11 Retina ] css3/blending/background-blend-mode-gif-color-2.html [ Failure Pass ]
 
 crbug.com/729836 [ Win ] fast/workers/worker-document-leak.html [ Pass Failure ]
+
+crbug.com/733071 [ Mac ] css3/blending/svg-blend-multiply-alpha.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
index cb85120..22ad39c5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
@@ -1,58 +1,58 @@
 This is a testharness.js-based test.
-Found 78 tests; 8 PASS, 70 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 78 tests; 54 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS DOMMatrix stringifier: identity (2d) 
 PASS DOMMatrix stringifier: identity (3d) 
-FAIL DOMMatrix stringifier: NaN (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: NaN (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: Infinity (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: Infinity (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: -Infinity (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: -Infinity (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrix stringifier: 1/3 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0.3333333333333333)" but got "matrix(1, 0, 0, 1, 0, 0.333333)"
-FAIL DOMMatrix stringifier: 1/3 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.3333333333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.333333, 0, 1)"
-FAIL DOMMatrix stringifier: 1/300000 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0.0000033333333333333333)" but got "matrix(1, 0, 0, 1, 0, 3.33333e-06)"
-FAIL DOMMatrix stringifier: 1/300000 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.0000033333333333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.33333e-06, 0, 1)"
-FAIL DOMMatrix stringifier: 1/300000000 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 3.3333333333333334e-9)" but got "matrix(1, 0, 0, 1, 0, 3.33333e-09)"
-FAIL DOMMatrix stringifier: 1/300000000 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.3333333333333334e-9, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.33333e-09, 0, 1)"
-FAIL DOMMatrix stringifier: 100000 + (1/3) (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 100000.33333333333)" but got "matrix(1, 0, 0, 1, 0, 100000)"
-FAIL DOMMatrix stringifier: 100000 + (1/3) (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 100000.33333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 100000, 0, 1)"
-FAIL DOMMatrix stringifier: Math.pow(2, 53) + 1 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 9007199254740992)" but got "matrix(1, 0, 0, 1, 0, 9.0072e+15)"
-FAIL DOMMatrix stringifier: Math.pow(2, 53) + 1 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9007199254740992, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9.0072e+15, 0, 1)"
-FAIL DOMMatrix stringifier: Math.pow(2, 53) + 2 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 9007199254740994)" but got "matrix(1, 0, 0, 1, 0, 9.0072e+15)"
-FAIL DOMMatrix stringifier: Math.pow(2, 53) + 2 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9007199254740994, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9.0072e+15, 0, 1)"
-FAIL DOMMatrix stringifier: Number.MAX_VALUE (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 1.7976931348623157e+308)" but got "matrix(1, 0, 0, 1, 0, 1.79769e+308)"
-FAIL DOMMatrix stringifier: Number.MAX_VALUE (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.7976931348623157e+308, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.79769e+308, 0, 1)"
-FAIL DOMMatrix stringifier: Number.MIN_VALUE (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 5e-324)" but got "matrix(1, 0, 0, 1, 0, 4.94066e-324)"
-FAIL DOMMatrix stringifier: Number.MIN_VALUE (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 5e-324, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 4.94066e-324, 0, 1)"
+PASS DOMMatrix stringifier: NaN (2d) 
+PASS DOMMatrix stringifier: NaN (3d) 
+PASS DOMMatrix stringifier: Infinity (2d) 
+PASS DOMMatrix stringifier: Infinity (3d) 
+PASS DOMMatrix stringifier: -Infinity (2d) 
+PASS DOMMatrix stringifier: -Infinity (3d) 
+PASS DOMMatrix stringifier: 1/3 (2d) 
+PASS DOMMatrix stringifier: 1/3 (3d) 
+PASS DOMMatrix stringifier: 1/300000 (2d) 
+PASS DOMMatrix stringifier: 1/300000 (3d) 
+PASS DOMMatrix stringifier: 1/300000000 (2d) 
+PASS DOMMatrix stringifier: 1/300000000 (3d) 
+PASS DOMMatrix stringifier: 100000 + (1/3) (2d) 
+PASS DOMMatrix stringifier: 100000 + (1/3) (3d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (2d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (3d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (2d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (3d) 
+PASS DOMMatrix stringifier: Number.MAX_VALUE (2d) 
+PASS DOMMatrix stringifier: Number.MAX_VALUE (3d) 
+PASS DOMMatrix stringifier: Number.MIN_VALUE (2d) 
+PASS DOMMatrix stringifier: Number.MIN_VALUE (3d) 
 PASS DOMMatrix stringifier: throwing getters (2d) 
 PASS DOMMatrix stringifier: throwing getters (3d) 
 PASS DOMMatrixReadOnly stringifier: identity (2d) 
 PASS DOMMatrixReadOnly stringifier: identity (3d) 
-FAIL DOMMatrixReadOnly stringifier: NaN (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: NaN (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: Infinity (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: Infinity (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: -Infinity (2d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: -Infinity (3d) assert_throws: function "() => String(matrix)" did not throw
-FAIL DOMMatrixReadOnly stringifier: 1/3 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0.3333333333333333)" but got "matrix(1, 0, 0, 1, 0, 0.333333)"
-FAIL DOMMatrixReadOnly stringifier: 1/3 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.3333333333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.333333, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: 1/300000 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0.0000033333333333333333)" but got "matrix(1, 0, 0, 1, 0, 3.33333e-06)"
-FAIL DOMMatrixReadOnly stringifier: 1/300000 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.0000033333333333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.33333e-06, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: 1/300000000 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 3.3333333333333334e-9)" but got "matrix(1, 0, 0, 1, 0, 3.33333e-09)"
-FAIL DOMMatrixReadOnly stringifier: 1/300000000 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.3333333333333334e-9, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 3.33333e-09, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: 100000 + (1/3) (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 100000.33333333333)" but got "matrix(1, 0, 0, 1, 0, 100000)"
-FAIL DOMMatrixReadOnly stringifier: 100000 + (1/3) (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 100000.33333333333, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 100000, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 9007199254740992)" but got "matrix(1, 0, 0, 1, 0, 9.0072e+15)"
-FAIL DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9007199254740992, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9.0072e+15, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 9007199254740994)" but got "matrix(1, 0, 0, 1, 0, 9.0072e+15)"
-FAIL DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9007199254740994, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 9.0072e+15, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: Number.MAX_VALUE (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 1.7976931348623157e+308)" but got "matrix(1, 0, 0, 1, 0, 1.79769e+308)"
-FAIL DOMMatrixReadOnly stringifier: Number.MAX_VALUE (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.7976931348623157e+308, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.79769e+308, 0, 1)"
-FAIL DOMMatrixReadOnly stringifier: Number.MIN_VALUE (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 5e-324)" but got "matrix(1, 0, 0, 1, 0, 4.94066e-324)"
-FAIL DOMMatrixReadOnly stringifier: Number.MIN_VALUE (3d) assert_equals: expected "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 5e-324, 0, 1)" but got "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 4.94066e-324, 0, 1)"
+PASS DOMMatrixReadOnly stringifier: NaN (2d) 
+PASS DOMMatrixReadOnly stringifier: NaN (3d) 
+PASS DOMMatrixReadOnly stringifier: Infinity (2d) 
+PASS DOMMatrixReadOnly stringifier: Infinity (3d) 
+PASS DOMMatrixReadOnly stringifier: -Infinity (2d) 
+PASS DOMMatrixReadOnly stringifier: -Infinity (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/3 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/3 (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000 (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000000 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000000 (3d) 
+PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (2d) 
+PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (3d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (2d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (3d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (2d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (3d) 
+PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (2d) 
+PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (3d) 
+PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (2d) 
+PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (3d) 
 PASS DOMMatrixReadOnly stringifier: throwing getters (2d) 
 PASS DOMMatrixReadOnly stringifier: throwing getters (3d) 
-FAIL WebKitCSSMatrix stringifier: identity (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0)" but got "matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)"
+PASS WebKitCSSMatrix stringifier: identity (2d) 
 FAIL WebKitCSSMatrix stringifier: identity (3d) self[constr].fromMatrix is not a function
 FAIL WebKitCSSMatrix stringifier: NaN (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,NaN'.
 FAIL WebKitCSSMatrix stringifier: NaN (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,NaN,0,1'.
@@ -76,7 +76,7 @@
 FAIL WebKitCSSMatrix stringifier: Number.MAX_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,1.7976931348623157e+308,0,1'.
 FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,5e-324'.
 FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,5e-324,0,1'.
-FAIL WebKitCSSMatrix stringifier: throwing getters (2d) assert_equals: expected "matrix(1, 0, 0, 1, 0, 0)" but got "matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)"
+PASS WebKitCSSMatrix stringifier: throwing getters (2d) 
 FAIL WebKitCSSMatrix stringifier: throwing getters (3d) self[constr].fromMatrix is not a function
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
index 87db6a2..7d0014c 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
@@ -5,10 +5,10 @@
 PASS Audio.prototype.toString.call(new Audio) is '[object HTMLAudioElement]'
 PASS Image.prototype.toString.call(new Image) is '[object HTMLImageElement]'
 PASS Option.prototype.toString.call(new Option) is '[object HTMLOptionElement]'
-PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix) is 'matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)'
-PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix()) is 'matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)'
+PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix) is 'matrix(1, 0, 0, 1, 0, 0)'
+PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix()) is 'matrix(1, 0, 0, 1, 0, 0)'
 PASS new WebKitCSSMatrix(null) threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'null'..
-FAIL new WebKitCSSMatrix(undefined) should throw an exception. Was matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000).
+FAIL new WebKitCSSMatrix(undefined) should throw an exception. Was matrix(1, 0, 0, 1, 0, 0).
 PASS XMLHttpRequest.prototype.toString.call(new XMLHttpRequest) is '[object XMLHttpRequest]'
 PASS XSLTProcessor.prototype.toString.call(new XSLTProcessor) is '[object XSLTProcessor]'
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors.html b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors.html
index cb07098..b18f462 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors.html
@@ -14,8 +14,8 @@
     shouldBe("Audio.prototype.toString.call(new Audio)", "'[object HTMLAudioElement]'");
     shouldBe("Image.prototype.toString.call(new Image)", "'[object HTMLImageElement]'");
     shouldBe("Option.prototype.toString.call(new Option)", "'[object HTMLOptionElement]'");
-    shouldBe("WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix)", "'matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)'");
-    shouldBe("WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix())", "'matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)'");
+    shouldBe("WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix)", "'matrix(1, 0, 0, 1, 0, 0)'");
+    shouldBe("WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix())", "'matrix(1, 0, 0, 1, 0, 0)'");
     shouldThrow("new WebKitCSSMatrix(null)");
     shouldThrow("new WebKitCSSMatrix(undefined)");
     shouldBe("XMLHttpRequest.prototype.toString.call(new XMLHttpRequest)", "'[object XMLHttpRequest]'");
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
index 3aa6eb3..1f3886d 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly.html
@@ -60,19 +60,41 @@
 }, "DOMMatrixReadOnly(transformList) - transformList");
 
 test(() => {
-  var matrix2d = new DOMMatrixReadOnly([1, 2, 3, 3.1, 2, 1]);
+  var matrix2d = new DOMMatrixReadOnly([1, 2, 3, 3.1, 2, 1.1]);
   assert_true(matrix2d.is2D);
-  assert_equals(matrix2d.toString(), "matrix(1, 2, 3, 3.1, 2, 1)");
+  assert_equals(matrix2d.toString(), "matrix(1, 2, 3, 3.1, 2, 1.1)");
 }, "DOMMatrixReadOnly toString() - 2D matrix");
 
 test(() => {
   var matrix3d = new DOMMatrixReadOnly([1, 2, 3, 4, 5, 6, 7, 8, 9, 10.1, 11, 12, 13, 14, 15, 16.6]);
   assert_false(matrix3d.is2D);
+
   assert_equals(matrix3d.toString(), "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10.1, 11, 12, 13, 14, 15, 16.6)");
 }, "DOMMatrixReadOnly toString() - 3D matrix");
 
-test(() => {
-  var identity_matrix = DOMMatrixReadOnly.fromMatrix();
+[NaN, Infinity, -Infinity].forEach(num => {
+  test(() => {
+    for (let i = 0; i < 6; ++i) {
+      let seq = [1, 0, 0, 1, 0, 0];
+      seq[i] = num;
+      var matrix2d = new DOMMatrixReadOnly(seq, 6);
+      assert_true(matrix2d.is2D);
+      assert_throws("InvalidStateError", () => { matrix2d.toString() });
+    }
+  }, `DOMMatrixReadOnly toString() - 2D matrix with ${num}`);
+
+  test(() => {
+    for (let i = 0; i < 12; ++i) {
+      let seq = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+      seq[i] = num;
+      var matrix3d = new DOMMatrixReadOnly(seq, 12);
+      assert_false(matrix3d.is2D);
+      assert_throws("InvalidStateError", () => { matrix3d.toString() });
+    }
+  }, `DOMMatrixReadOnly toString() - 3D matrix with ${num}`);
+});
+
+test(() => {  var identity_matrix = DOMMatrixReadOnly.fromMatrix();
   assert_true(identity_matrix.is2D);
   assert_object_equals(identity_matrix.toJSON(),
     { a : 1, b : 0, c : 0, d : 1, e : 0, f : 0,
diff --git a/third_party/WebKit/LayoutTests/fast/filesystem/resources/file-writer-events.js b/third_party/WebKit/LayoutTests/fast/filesystem/resources/file-writer-events.js
index 3c9dcfb..fcd6880c6 100644
--- a/third_party/WebKit/LayoutTests/fast/filesystem/resources/file-writer-events.js
+++ b/third_party/WebKit/LayoutTests/fast/filesystem/resources/file-writer-events.js
@@ -86,7 +86,11 @@
     blob = tenXBlob(blob);
     toBeWritten = blob.size;
     writer = fileWriter;
-    fileWriter.onerror = onError;
+    fileWriter.onerror = function(e) {
+      debug(fileWriter.error.name);
+      debug(fileWriter.error.message);
+      onError(e);
+    };
     fileWriter.onwritestart = onWriteStart;
     fileWriter.onprogress = onProgress;
     fileWriter.onwrite = onWrite;
diff --git a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
index 4ea755b..cdd5578c 100644
--- a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
+++ b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
Binary files differ
diff --git a/third_party/WebKit/Source/core/animation/Animation.cpp b/third_party/WebKit/Source/core/animation/Animation.cpp
index 6950a92..9fea296 100644
--- a/third_party/WebKit/Source/core/animation/Animation.cpp
+++ b/third_party/WebKit/Source/core/animation/Animation.cpp
@@ -756,11 +756,10 @@
     if (target_element->GetLayoutObject() &&
         target_element->GetLayoutObject()->IsBoxModelObject() &&
         target_element->GetLayoutObject()->HasLayer()) {
-      PaintLayer* paint_layer =
-          ToLayoutBoxModelObject(target_element->GetLayoutObject())->Layer();
       CompositorElementId target_element_id =
-          CompositorElementIdFromPaintLayerId(
-              paint_layer->UniqueId(), CompositorElementIdNamespace::kPrimary);
+          CompositorElementIdFromLayoutObjectId(
+              target_element->GetLayoutObject()->UniqueId(),
+              CompositorElementIdNamespace::kPrimary);
       if (!composited_element_ids->Contains(target_element_id))
         return false;
     } else {
diff --git a/third_party/WebKit/Source/core/animation/AnimationTest.cpp b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
index 14fc316c..1aaf462 100644
--- a/third_party/WebKit/Source/core/animation/AnimationTest.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationTest.cpp
@@ -807,8 +807,8 @@
   Optional<CompositorElementIdSet> composited_element_ids =
       CompositorElementIdSet();
   CompositorElementId expected_compositor_element_id =
-      CompositorElementIdFromPaintLayerId(
-          ToLayoutBoxModelObject(object_composited)->Layer()->UniqueId(),
+      CompositorElementIdFromLayoutObjectId(
+          ToLayoutBoxModelObject(object_composited)->UniqueId(),
           CompositorElementIdNamespace::kPrimary);
   composited_element_ids->insert(expected_compositor_element_id);
 
diff --git a/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp b/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp
index 7b2c1ea..d450391 100644
--- a/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp
@@ -467,8 +467,9 @@
   }
 
   CompositorAnimationPlayer* compositor_player = animation.CompositorPlayer();
-  compositor_player->AttachElement(CompositorElementIdFromPaintLayerId(
-      layer->UniqueId(), CompositorElementIdNamespace::kPrimary));
+  compositor_player->AttachElement(CompositorElementIdFromLayoutObjectId(
+      element.GetLayoutObject()->UniqueId(),
+      CompositorElementIdNamespace::kPrimary));
 }
 
 bool CompositorAnimations::ConvertTimingForCompositor(
diff --git a/third_party/WebKit/Source/core/css/CSSMatrix.cpp b/third_party/WebKit/Source/core/css/CSSMatrix.cpp
index ed126c8..227272c 100644
--- a/third_party/WebKit/Source/core/css/CSSMatrix.cpp
+++ b/third_party/WebKit/Source/core/css/CSSMatrix.cpp
@@ -193,12 +193,12 @@
   // FIXME - Need to ensure valid CSS floating point values
   // (https://bugs.webkit.org/show_bug.cgi?id=20674)
   if (matrix_->IsAffine())
-    return String::Format("matrix(%f, %f, %f, %f, %f, %f)", matrix_->A(),
+    return String::Format("matrix(%g, %g, %g, %g, %g, %g)", matrix_->A(),
                           matrix_->B(), matrix_->C(), matrix_->D(),
                           matrix_->E(), matrix_->F());
   return String::Format(
-      "matrix3d(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, "
-      "%f)",
+      "matrix3d(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, "
+      "%g)",
       matrix_->M11(), matrix_->M12(), matrix_->M13(), matrix_->M14(),
       matrix_->M21(), matrix_->M22(), matrix_->M23(), matrix_->M24(),
       matrix_->M31(), matrix_->M32(), matrix_->M33(), matrix_->M34(),
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
index 4290ed2..4c7d6ed 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.cpp
@@ -322,21 +322,83 @@
   return NotShared<DOMFloat64Array>(DOMFloat64Array::Create(array, 16));
 }
 
-const String DOMMatrixReadOnly::toString() const {
-  std::stringstream stream;
-  if (is2D()) {
-    stream << "matrix(" << a() << ", " << b() << ", " << c() << ", " << d()
-           << ", " << e() << ", " << f();
-  } else {
-    stream << "matrix3d(" << m11() << ", " << m12() << ", " << m13() << ", "
-           << m14() << ", " << m21() << ", " << m22() << ", " << m23() << ", "
-           << m24() << ", " << m31() << ", " << m32() << ", " << m33() << ", "
-           << m34() << ", " << m41() << ", " << m42() << ", " << m43() << ", "
-           << m44();
-  }
-  stream << ")";
+const String DOMMatrixReadOnly::toString(
+    ExceptionState& exception_state) const {
+  const char* kComma = ", ";
+  String result;
 
-  return String(stream.str().c_str());
+  if (is2D()) {
+    if (!std::isfinite(a()) || !std::isfinite(b()) || !std::isfinite(c()) ||
+        !std::isfinite(d()) || !std::isfinite(e()) || !std::isfinite(f())) {
+      exception_state.ThrowDOMException(
+          kInvalidStateError,
+          "DOMMatrix cannot be serialized with NaN or Infinity values.");
+      return String();
+    }
+
+    result.append("matrix(");
+    result.append(String::NumberToStringECMAScript(a()));
+    result.append(kComma);
+    result.append(String::NumberToStringECMAScript(b()));
+    result.append(kComma);
+    result.append(String::NumberToStringECMAScript(c()));
+    result.append(kComma);
+    result.append(String::NumberToStringECMAScript(d()));
+    result.append(kComma);
+    result.append(String::NumberToStringECMAScript(e()));
+    result.append(kComma);
+    result.append(String::NumberToStringECMAScript(f()));
+    result.append(")");
+    return result;
+  }
+
+  if (!std::isfinite(m11()) || !std::isfinite(m12()) || !std::isfinite(m13()) ||
+      !std::isfinite(m14()) || !std::isfinite(m21()) || !std::isfinite(m22()) ||
+      !std::isfinite(m23()) || !std::isfinite(m24()) || !std::isfinite(m31()) ||
+      !std::isfinite(m32()) || !std::isfinite(m33()) || !std::isfinite(m34()) ||
+      !std::isfinite(m41()) || !std::isfinite(m42()) || !std::isfinite(m43()) ||
+      !std::isfinite(m44())) {
+    exception_state.ThrowDOMException(
+        kInvalidStateError,
+        "DOMMatrix cannot be serialized with NaN or Infinity values.");
+    return String();
+  }
+
+  result.append("matrix3d(");
+  result.append(String::NumberToStringECMAScript(m11()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m12()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m13()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m14()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m21()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m22()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m23()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m24()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m31()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m32()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m33()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m34()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m41()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m42()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m43()));
+  result.append(kComma);
+  result.append(String::NumberToStringECMAScript(m44()));
+  result.append(")");
+
+  return result;
 }
 
 ScriptValue DOMMatrixReadOnly::toJSONForBinding(
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
index b26da77..5c6bfb6 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.h
@@ -98,7 +98,7 @@
   NotShared<DOMFloat32Array> toFloat32Array() const;
   NotShared<DOMFloat64Array> toFloat64Array() const;
 
-  const String toString() const;
+  const String toString(ExceptionState&) const;
 
   ScriptValue toJSONForBinding(ScriptState*) const;
 
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.idl b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.idl
index 1032f29..56a29507 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrixReadOnly.idl
@@ -76,6 +76,6 @@
     DOMPoint transformPoint(optional DOMPointInit point);
     Float32Array toFloat32Array();
     Float64Array toFloat64Array();
-    stringifier;
+    [RaisesException] stringifier;
     serializer = { attribute };
 };
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index f583a55a..a5983d54 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -3680,10 +3680,14 @@
   // fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html
   GetMediaControls()->Reset();
 
-  if (ShouldShowControls(RecordMetricsBehavior::kDoRecord))
+  bool native_controls = ShouldShowControls(RecordMetricsBehavior::kDoRecord);
+  if (native_controls)
     GetMediaControls()->MaybeShow();
   else
     GetMediaControls()->Hide();
+
+  if (web_media_player_)
+    web_media_player_->OnHasNativeControlsChanged(native_controls);
 }
 
 CueTimeline& HTMLMediaElement::GetCueTimeline() {
@@ -4045,6 +4049,10 @@
   }
 }
 
+bool HTMLMediaElement::HasNativeControls() {
+  return ShouldShowControls(RecordMetricsBehavior::kDoRecord);
+}
+
 void HTMLMediaElement::CheckViewportIntersectionTimerFired(TimerBase*) {
   bool should_report_root_bounds = true;
   IntersectionGeometry geometry(nullptr, *this, Vector<Length>(),
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index d1e2bd2..bce174e6 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -419,6 +419,7 @@
   WebMediaPlayer::TrackId GetSelectedVideoTrackId() final;
   bool IsAutoplayingMuted() final;
   void ActivateViewportIntersectionMonitoring(bool) final;
+  bool HasNativeControls() final;
 
   void LoadTimerFired(TimerBase*);
   void ProgressEventTimerFired(TimerBase*);
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index f8c02f8c..7a4a215 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -405,6 +405,11 @@
     return rare_paint_data_ ? rare_paint_data_->PaintProperties() : nullptr;
   }
 
+  LayoutObjectId UniqueId() const {
+    DCHECK(rare_paint_data_);
+    return rare_paint_data_ ? rare_paint_data_->UniqueId() : 0;
+  }
+
   // The complete set of property nodes that should be used as a starting point
   // to paint this LayoutObject. See also the comment for
   // RarePaintData::local_border_box_properties_.
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index bea0b2b..6092e73 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -2275,8 +2275,9 @@
                                  CompositorMutableProperty::kScrollTop) &
                                 compositor_mutable_properties;
   } else {
-    element_id = CompositorElementIdFromPaintLayerId(
-        owning_layer_.UniqueId(), CompositorElementIdNamespace::kPrimary);
+    element_id = CompositorElementIdFromLayoutObjectId(
+        owning_layer_.GetLayoutObject().UniqueId(),
+        CompositorElementIdNamespace::kPrimary);
   }
 
   graphics_layer_->SetElementId(element_id);
@@ -2410,8 +2411,9 @@
             DOMNodeIds::IdForNode(data.owning_node),
             CompositorElementIdNamespace::kScrollCompositorProxy);
       } else {
-        element_id = CompositorElementIdFromPaintLayerId(
-            owning_layer_.UniqueId(), CompositorElementIdNamespace::kScroll);
+        element_id = CompositorElementIdFromLayoutObjectId(
+            owning_layer_.GetLayoutObject().UniqueId(),
+            CompositorElementIdNamespace::kScroll);
       }
 
       scrolling_contents_layer_->SetElementId(element_id);
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
index 3fe3c0b..c63e8c6 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include "core/CoreExport.h"
 #include "platform/geometry/LayoutPoint.h"
-#include "platform/graphics/CompositorElementId.h"
 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
 #include "platform/graphics/paint/EffectPaintPropertyNode.h"
 #include "platform/graphics/paint/PaintChunkProperties.h"
@@ -275,13 +274,6 @@
   }
 #endif
 
-  CompositorElementId& GetCompositorElementId() {
-    return compositor_element_id_;
-  }
-  void SetCompositorElementId(const CompositorElementId& id) {
-    compositor_element_id_ = id;
-  }
-
  private:
   ObjectPaintProperties() {}
 
@@ -327,7 +319,6 @@
   RefPtr<TransformPaintPropertyNode> svg_local_to_border_box_transform_;
   RefPtr<TransformPaintPropertyNode> scroll_translation_;
   RefPtr<TransformPaintPropertyNode> scrollbar_paint_offset_;
-  CompositorElementId compositor_element_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index a1b946e2..f3a77ae 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -105,7 +105,6 @@
     void* pointer;
     LayoutRect rect;
   } previous_paint_status;
-  uint64_t unique_id;
 };
 
 static_assert(sizeof(PaintLayer) == sizeof(SameSizeAsPaintLayer),
@@ -168,8 +167,6 @@
       static_inline_position_(0),
       static_block_position_(0),
       ancestor_overflow_layer_(nullptr) {
-  static PaintLayerId paint_layer_id_counter = 0;
-  unique_id_ = ++paint_layer_id_counter;
   UpdateStackingNode();
 
   is_self_painting_layer_ = ShouldBeSelfPaintingLayer();
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index f1630b4..71fab18 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -1025,9 +1025,6 @@
   void EndShouldKeepAliveAllClientsRecursive();
 #endif
 
-  // An id for this PaintLayer that is unique for the lifetime of the WebView.
-  PaintLayerId UniqueId() const { return unique_id_; }
-
  private:
   void SetNeedsCompositingInputsUpdateInternal();
 
@@ -1275,8 +1272,6 @@
 
   std::unique_ptr<PaintLayerRareData> rare_data_;
 
-  PaintLayerId unique_id_;
-
   FRIEND_TEST_ALL_PREFIXES(PaintLayerTest,
                            DescendantDependentFlagsStopsAtThrottledFrames);
   FRIEND_TEST_ALL_PREFIXES(PaintLayerTest,
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 8f109f6..6a65d096 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -72,14 +72,6 @@
   return true;
 }
 
-static CompositorElementId CreatePaintLayereBasedCompositorElementId(
-    const LayoutObject& object) {
-  DCHECK(object.IsBoxModelObject() && object.HasLayer());
-  return CompositorElementIdFromPaintLayerId(
-      ToLayoutBoxModelObject(object).Layer()->UniqueId(),
-      CompositorElementIdNamespace::kPrimary);
-}
-
 // True if a new property was created or a main thread scrolling reason changed
 // (which can affect descendants), false if an existing one was updated.
 static bool UpdateScrollTranslation(
@@ -95,24 +87,27 @@
     MainThreadScrollingReasons main_thread_scrolling_reasons,
     WebLayerScrollClient* scroll_client) {
   DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled());
-  CompositorElementId compositor_element_id =
-      CreatePaintLayereBasedCompositorElementId(*frame_view.GetLayoutView());
   if (auto* existing_scroll_translation = frame_view.ScrollTranslation()) {
     auto existing_reasons = existing_scroll_translation->ScrollNode()
                                 ->GetMainThreadScrollingReasons();
     existing_scroll_translation->UpdateScrollTranslation(
         std::move(parent), matrix, origin, false, 0, kCompositingReasonNone,
-        compositor_element_id, std::move(scroll_parent), clip, bounds,
-        user_scrollable_horizontal, user_scrollable_vertical,
-        main_thread_scrolling_reasons, scroll_client);
+        CompositorElementIdFromLayoutObjectId(
+            frame_view.GetLayoutView()->UniqueId(),
+            CompositorElementIdNamespace::kScrollTranslation),
+        std::move(scroll_parent), clip, bounds, user_scrollable_horizontal,
+        user_scrollable_vertical, main_thread_scrolling_reasons, scroll_client);
     return existing_reasons != main_thread_scrolling_reasons;
   }
   frame_view.SetScrollTranslation(
       TransformPaintPropertyNode::CreateScrollTranslation(
           std::move(parent), matrix, origin, false, 0, kCompositingReasonNone,
-          compositor_element_id, std::move(scroll_parent), clip, bounds,
-          user_scrollable_horizontal, user_scrollable_vertical,
-          main_thread_scrolling_reasons, scroll_client));
+          CompositorElementIdFromLayoutObjectId(
+              frame_view.GetLayoutView()->UniqueId(),
+              CompositorElementIdNamespace::kScrollTranslation),
+          std::move(scroll_parent), clip, bounds, user_scrollable_horizontal,
+          user_scrollable_vertical, main_thread_scrolling_reasons,
+          scroll_client));
   return true;
 }
 
@@ -429,7 +424,8 @@
           context.current.transform, matrix, TransformOrigin(box),
           context.current.should_flatten_inherited_transform,
           rendering_context_id, compositing_reasons,
-          properties.GetCompositorElementId());
+          CompositorElementIdFromLayoutObjectId(
+              object.UniqueId(), CompositorElementIdNamespace::kPrimary));
     } else {
       force_subtree_update |= properties.ClearTransform();
     }
@@ -589,11 +585,12 @@
 
       DCHECK(!style.HasCurrentOpacityAnimation() ||
              compositing_reasons != kCompositingReasonNone);
-
       force_subtree_update |= properties.UpdateEffect(
           context.current_effect, context.current.transform, output_clip,
           kColorFilterNone, CompositorFilterOperations(), style.Opacity(),
-          blend_mode, compositing_reasons, properties.GetCompositorElementId());
+          blend_mode, compositing_reasons,
+          CompositorElementIdFromLayoutObjectId(
+              object.UniqueId(), CompositorElementIdNamespace::kPrimary));
       if (has_mask) {
         // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
         // grouping (i.e. descendant-dependent compositing reason) properly
@@ -604,7 +601,8 @@
             properties.Effect(), context.current.transform, output_clip,
             mask_color_filter, CompositorFilterOperations(), 1.f,
             SkBlendMode::kDstIn, kCompositingReasonSquashingDisallowed,
-            CompositorElementId());
+            CompositorElementIdFromLayoutObjectId(
+                object.UniqueId(), CompositorElementIdNamespace::kEffectMask));
       } else {
         force_subtree_update |= properties.ClearMask();
       }
@@ -683,7 +681,9 @@
       force_subtree_update |= properties.UpdateFilter(
           context.current_effect, context.current.transform, output_clip,
           kColorFilterNone, std::move(filter), 1.f, SkBlendMode::kSrcOver,
-          compositing_reasons, properties.GetCompositorElementId());
+          compositing_reasons,
+          CompositorElementIdFromLayoutObjectId(
+              object.UniqueId(), CompositorElementIdNamespace::kEffectFilter));
     } else {
       force_subtree_update |= properties.ClearFilter();
     }
@@ -958,9 +958,12 @@
           context.current.transform, matrix, FloatPoint3D(),
           context.current.should_flatten_inherited_transform,
           context.current.rendering_context_id, kCompositingReasonNone,
-          properties.GetCompositorElementId(), context.current.scroll,
-          scroll_clip, scroll_bounds, user_scrollable_horizontal,
-          user_scrollable_vertical, reasons, scrollable_area);
+          CompositorElementIdFromLayoutObjectId(
+              object.UniqueId(),
+              CompositorElementIdNamespace::kScrollTranslation),
+          context.current.scroll, scroll_clip, scroll_bounds,
+          user_scrollable_horizontal, user_scrollable_vertical, reasons,
+          scrollable_area);
     } else {
       // Ensure pre-existing properties are cleared.
       force_subtree_update |= properties.ClearScrollTranslation();
@@ -1167,12 +1170,7 @@
   bool had_paint_properties = object.PaintProperties();
 
   if (needs_paint_properties && !had_paint_properties) {
-    ObjectPaintProperties& paint_properties =
-        object.GetMutableForPainting().EnsurePaintProperties();
-    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && object.HasLayer()) {
-      paint_properties.SetCompositorElementId(
-          CreatePaintLayereBasedCompositorElementId(object));
-    }
+    object.GetMutableForPainting().EnsurePaintProperties();
   } else if (!needs_paint_properties && had_paint_properties) {
     object.GetMutableForPainting().ClearPaintProperties();
     full_context.force_subtree_update = true;
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.cpp b/third_party/WebKit/Source/core/paint/RarePaintData.cpp
index 1a47a61..310097a 100644
--- a/third_party/WebKit/Source/core/paint/RarePaintData.cpp
+++ b/third_party/WebKit/Source/core/paint/RarePaintData.cpp
@@ -9,7 +9,10 @@
 
 namespace blink {
 
-RarePaintData::RarePaintData() {}
+RarePaintData::RarePaintData() {
+  static LayoutObjectId layout_object_id_counter = 0;
+  unique_id_ = ++layout_object_id_counter;
+}
 
 RarePaintData::~RarePaintData() {}
 
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.h b/third_party/WebKit/Source/core/paint/RarePaintData.h
index 9353473..4d094d9 100644
--- a/third_party/WebKit/Source/core/paint/RarePaintData.h
+++ b/third_party/WebKit/Source/core/paint/RarePaintData.h
@@ -51,6 +51,9 @@
   // to contents.
   PropertyTreeState ContentsProperties() const;
 
+  // An id for this object that is unique for the lifetime of the WebView.
+  LayoutObjectId UniqueId() const { return unique_id_; }
+
  private:
   // The PaintLayer associated with this LayoutBoxModelObject. This can be null
   // depending on the return value of LayoutBoxModelObject::layerTypeRequired().
@@ -69,6 +72,8 @@
   //   properties would have a transform node that points to the div's
   //   ancestor transform space.
   std::unique_ptr<PropertyTreeState> local_border_box_properties_;
+
+  LayoutObjectId unique_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/BUILD.gn b/third_party/WebKit/Source/modules/BUILD.gn
index 21cd314..8340567 100644
--- a/third_party/WebKit/Source/modules/BUILD.gn
+++ b/third_party/WebKit/Source/modules/BUILD.gn
@@ -290,8 +290,8 @@
     "payments/CompleteTest.cpp",
     "payments/OnPaymentResponseTest.cpp",
     "payments/PaymentAddressTest.cpp",
-    "payments/PaymentAppRequestConversionTest.cpp",
     "payments/PaymentRequestDetailsTest.cpp",
+    "payments/PaymentRequestEventDataConversionTest.cpp",
     "payments/PaymentRequestTest.cpp",
     "payments/PaymentRequestUpdateEventTest.cpp",
     "payments/PaymentResponseTest.cpp",
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.cpp b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
index 3b5988c..9b48bb3b 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.cpp
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
@@ -117,7 +117,9 @@
   HeapVector<Member<BudgetState>> budget(expectations.size());
   for (size_t i = 0; i < expectations.size(); i++) {
     // Return the largest integer less than the budget, so it's easier for
-    // developer to reason about budget.
+    // developer to reason about budget. Flooring is also significant from a
+    // privacy perspective, as we don't want to share precise data as it could
+    // aid fingerprinting. See https://crbug.com/710809.
     budget[i] = new BudgetState(floor(expectations[i]->budget_at),
                                 expectations[i]->time);
   }
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
index f571a762..d20f9c6 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -72,6 +72,7 @@
 #include "modules/remoteplayback/HTMLMediaElementRemotePlayback.h"
 #include "modules/remoteplayback/RemotePlayback.h"
 #include "platform/EventDispatchForbiddenScope.h"
+#include "platform/RuntimeEnabledFeatures.h"
 
 namespace blink {
 
@@ -742,7 +743,8 @@
     // non-cast changes (e.g., resize) occur.  If the panel button
     // is shown, however, compute...() will take control of the
     // overlay cast button if it needs to hide it from the panel.
-    overlay_cast_button_->TryShowOverlay();
+    if (RuntimeEnabledFeatures::mediaCastOverlayButtonEnabled())
+      overlay_cast_button_->TryShowOverlay();
     cast_button_->SetIsWanted(false);
   } else if (MediaElement().ShouldShowControls()) {
     overlay_cast_button_->SetIsWanted(false);
@@ -752,7 +754,8 @@
 
 void MediaControlsImpl::ShowOverlayCastButtonIfNeeded() {
   if (MediaElement().ShouldShowControls() ||
-      !ShouldShowCastButton(MediaElement())) {
+      !ShouldShowCastButton(MediaElement()) ||
+      !RuntimeEnabledFeatures::mediaCastOverlayButtonEnabled()) {
     return;
   }
 
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
index 056b9a2..2d97725 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
@@ -27,6 +27,7 @@
 #include "modules/media_controls/elements/MediaControlVolumeSliderElement.h"
 #include "modules/remoteplayback/HTMLMediaElementRemotePlayback.h"
 #include "modules/remoteplayback/RemotePlayback.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/heap/Handle.h"
 #include "platform/testing/EmptyWebMediaPlayer.h"
 #include "platform/testing/HistogramTester.h"
@@ -141,7 +142,12 @@
 
 class MediaControlsImplTest : public ::testing::Test {
  protected:
-  virtual void SetUp() { InitializePage(); }
+  virtual void SetUp() {
+    // Enable the cast overlay button as this is enabled by default.
+    RuntimeEnabledFeatures::setMediaCastOverlayButtonEnabled(true);
+
+    InitializePage();
+  }
 
   void InitializePage() {
     Page::PageClients clients;
@@ -357,6 +363,17 @@
   ASSERT_TRUE(IsElementVisible(*cast_overlay_button));
 }
 
+TEST_F(MediaControlsImplTest, CastOverlayDisabled) {
+  RuntimeEnabledFeatures::setMediaCastOverlayButtonEnabled(false);
+
+  Element* cast_overlay_button = GetElementByShadowPseudoId(
+      MediaControls(), "-internal-media-controls-overlay-cast-button");
+  ASSERT_NE(nullptr, cast_overlay_button);
+
+  SimulateRouteAvailable();
+  ASSERT_FALSE(IsElementVisible(*cast_overlay_button));
+}
+
 TEST_F(MediaControlsImplTest, CastOverlayDisableRemotePlaybackAttr) {
   Element* cast_overlay_button = GetElementByShadowPseudoId(
       MediaControls(), "-internal-media-controls-overlay-cast-button");
@@ -393,6 +410,24 @@
   EXPECT_TRUE(IsElementVisible(*cast_overlay_button));
 }
 
+TEST_F(MediaControlsImplTest, CastOverlayDisabledMediaControlsDisabled) {
+  RuntimeEnabledFeatures::setMediaCastOverlayButtonEnabled(false);
+
+  Element* cast_overlay_button = GetElementByShadowPseudoId(
+      MediaControls(), "-internal-media-controls-overlay-cast-button");
+  ASSERT_NE(nullptr, cast_overlay_button);
+
+  EXPECT_FALSE(IsElementVisible(*cast_overlay_button));
+  SimulateRouteAvailable();
+  EXPECT_FALSE(IsElementVisible(*cast_overlay_button));
+
+  GetDocument().GetSettings()->SetMediaControlsEnabled(false);
+  EXPECT_FALSE(IsElementVisible(*cast_overlay_button));
+
+  GetDocument().GetSettings()->SetMediaControlsEnabled(true);
+  EXPECT_FALSE(IsElementVisible(*cast_overlay_button));
+}
+
 TEST_F(MediaControlsImplTest, KeepControlsVisibleIfOverflowListVisible) {
   Element* overflow_list = GetElementByShadowPseudoId(
       MediaControls(), "-internal-media-controls-overflow-menu-list");
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlCastButtonElement.cpp b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlCastButtonElement.cpp
index 279d449..fedd97f 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlCastButtonElement.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlCastButtonElement.cpp
@@ -100,9 +100,13 @@
     if (is_overlay_button_) {
       Platform::Current()->RecordAction(
           UserMetricsAction("Media.Controls.CastOverlay"));
+      Platform::Current()->RecordRapporURL("Media.Controls.CastOverlay",
+                                           WebURL(GetDocument().Url()));
     } else {
       Platform::Current()->RecordAction(
           UserMetricsAction("Media.Controls.Cast"));
+      Platform::Current()->RecordRapporURL("Media.Controls.Cast",
+                                           WebURL(GetDocument().Url()));
     }
 
     if (is_overlay_button_ && !click_use_counted_) {
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 0c3d259..f6a9a02 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -479,7 +479,7 @@
                     "payments/AndroidPayTokenization.idl",
                     "payments/BasicCardRequest.idl",
                     "payments/PaymentAppResponse.idl",
-                    "payments/PaymentAppRequest.idl",
+                    "payments/PaymentRequestEventInit.idl",
                     "payments/PaymentCurrencyAmount.idl",
                     "payments/PaymentDetailsBase.idl",
                     "payments/PaymentDetailsInit.idl",
@@ -533,6 +533,7 @@
                     "webaudio/AudioBufferSourceOptions.idl",
                     "webaudio/AudioContextOptions.idl",
                     "webaudio/AudioNodeOptions.idl",
+                    "webaudio/AudioParamDescriptor.idl",
                     "webaudio/AudioProcessingEventInit.idl",
                     "webaudio/AudioTimestamp.idl",
                     "webaudio/BiquadFilterOptions.idl",
@@ -860,8 +861,8 @@
   "$blink_modules_output_dir/payments/BasicCardRequest.h",
   "$blink_modules_output_dir/payments/PaymentAppResponse.cpp",
   "$blink_modules_output_dir/payments/PaymentAppResponse.h",
-  "$blink_modules_output_dir/payments/PaymentAppRequest.cpp",
-  "$blink_modules_output_dir/payments/PaymentAppRequest.h",
+  "$blink_modules_output_dir/payments/PaymentRequestEventInit.cpp",
+  "$blink_modules_output_dir/payments/PaymentRequestEventInit.h",
   "$blink_modules_output_dir/payments/PaymentCurrencyAmount.cpp",
   "$blink_modules_output_dir/payments/PaymentCurrencyAmount.h",
   "$blink_modules_output_dir/payments/PaymentDetailsBase.cpp",
@@ -968,6 +969,8 @@
   "$blink_modules_output_dir/webaudio/AudioContextOptions.h",
   "$blink_modules_output_dir/webaudio/AudioNodeOptions.cpp",
   "$blink_modules_output_dir/webaudio/AudioNodeOptions.h",
+  "$blink_modules_output_dir/webaudio/AudioParamDescriptor.cpp",
+  "$blink_modules_output_dir/webaudio/AudioParamDescriptor.h",
   "$blink_modules_output_dir/webaudio/AudioProcessingEventInit.cpp",
   "$blink_modules_output_dir/webaudio/AudioProcessingEventInit.h",
   "$blink_modules_output_dir/webaudio/AudioTimestamp.cpp",
diff --git a/third_party/WebKit/Source/modules/payments/BUILD.gn b/third_party/WebKit/Source/modules/payments/BUILD.gn
index 09cdb69..8488065 100644
--- a/third_party/WebKit/Source/modules/payments/BUILD.gn
+++ b/third_party/WebKit/Source/modules/payments/BUILD.gn
@@ -10,8 +10,6 @@
     "HTMLIFrameElementPayments.h",
     "PaymentAddress.cpp",
     "PaymentAddress.h",
-    "PaymentAppRequestConversion.cpp",
-    "PaymentAppRequestConversion.h",
     "PaymentAppServiceWorkerGlobalScope.h",
     "PaymentAppServiceWorkerRegistration.cpp",
     "PaymentAppServiceWorkerRegistration.h",
@@ -24,6 +22,8 @@
     "PaymentRequest.h",
     "PaymentRequestEvent.cpp",
     "PaymentRequestEvent.h",
+    "PaymentRequestEventDataConversion.cpp",
+    "PaymentRequestEventDataConversion.h",
     "PaymentRequestRespondWithObserver.cpp",
     "PaymentRequestRespondWithObserver.h",
     "PaymentRequestUpdateEvent.cpp",
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.h b/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.h
deleted file mode 100644
index 80b3c360..0000000
--- a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.h
+++ /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.
-
-#ifndef PaymentAppRequestConversion_h
-#define PaymentAppRequestConversion_h
-
-#include "modules/payments/PaymentAppRequest.h"
-#include "platform/wtf/Allocator.h"
-
-namespace blink {
-
-class ScriptState;
-struct WebPaymentAppRequest;
-
-class MODULES_EXPORT PaymentAppRequestConversion {
-  STATIC_ONLY(PaymentAppRequestConversion);
-
- public:
-  static PaymentAppRequest ToPaymentAppRequest(ScriptState*,
-                                               const WebPaymentAppRequest&);
-};
-
-}  // namespace blink
-
-#endif  // PaymentAppRequestConversion_h
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.cpp
index 6627c49..52788370 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.cpp
@@ -18,10 +18,10 @@
 
 PaymentRequestEvent* PaymentRequestEvent::Create(
     const AtomicString& type,
-    const PaymentAppRequest& app_request,
+    const PaymentRequestEventInit& initializer,
     RespondWithObserver* respond_with_observer,
     WaitUntilObserver* wait_until_observer) {
-  return new PaymentRequestEvent(type, app_request, respond_with_observer,
+  return new PaymentRequestEvent(type, initializer, respond_with_observer,
                                  wait_until_observer);
 }
 
@@ -119,17 +119,17 @@
 
 PaymentRequestEvent::PaymentRequestEvent(
     const AtomicString& type,
-    const PaymentAppRequest& app_request,
+    const PaymentRequestEventInit& initializer,
     RespondWithObserver* respond_with_observer,
     WaitUntilObserver* wait_until_observer)
     : ExtendableEvent(type, ExtendableEventInit(), wait_until_observer),
-      top_level_origin_(app_request.topLevelOrigin()),
-      payment_request_origin_(app_request.paymentRequestOrigin()),
-      payment_request_id_(app_request.paymentRequestId()),
-      method_data_(std::move(app_request.methodData())),
-      total_(app_request.total()),
-      modifiers_(app_request.modifiers()),
-      instrument_key_(app_request.instrumentKey()),
+      top_level_origin_(initializer.topLevelOrigin()),
+      payment_request_origin_(initializer.paymentRequestOrigin()),
+      payment_request_id_(initializer.paymentRequestId()),
+      method_data_(std::move(initializer.methodData())),
+      total_(initializer.total()),
+      modifiers_(initializer.modifiers()),
+      instrument_key_(initializer.instrumentKey()),
       observer_(respond_with_observer) {}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.h b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.h
index bac4817c..42be7d1 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.h
@@ -6,7 +6,7 @@
 #define PaymentRequestEvent_h
 
 #include "modules/EventModules.h"
-#include "modules/payments/PaymentAppRequest.h"
+#include "modules/payments/PaymentRequestEventInit.h"
 #include "modules/serviceworkers/ExtendableEvent.h"
 #include "platform/heap/Handle.h"
 
@@ -24,7 +24,7 @@
 
  public:
   static PaymentRequestEvent* Create(const AtomicString& type,
-                                     const PaymentAppRequest&,
+                                     const PaymentRequestEventInit&,
                                      RespondWithObserver*,
                                      WaitUntilObserver*);
   ~PaymentRequestEvent() override;
@@ -46,7 +46,7 @@
 
  private:
   PaymentRequestEvent(const AtomicString& type,
-                      const PaymentAppRequest&,
+                      const PaymentRequestEventInit&,
                       RespondWithObserver*,
                       WaitUntilObserver*);
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.cpp
similarity index 77%
rename from third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.cpp
rename to third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.cpp
index 8acdb04..6e7997c 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversion.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.cpp
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "modules/payments/PaymentAppRequestConversion.h"
+#include "modules/payments/PaymentRequestEventDataConversion.h"
 
 #include "bindings/core/v8/ToV8ForCore.h"
-#include "modules/payments/PaymentAppRequest.h"
 #include "modules/payments/PaymentCurrencyAmount.h"
 #include "modules/payments/PaymentDetailsModifier.h"
 #include "modules/payments/PaymentItem.h"
 #include "modules/payments/PaymentMethodData.h"
+#include "modules/payments/PaymentRequestEventInit.h"
 #include "platform/bindings/ScriptState.h"
-#include "public/platform/modules/payments/WebPaymentAppRequest.h"
 #include "public/platform/modules/payments/WebPaymentMethodData.h"
+#include "public/platform/modules/payments/WebPaymentRequestEventData.h"
 
 namespace blink {
 namespace {
@@ -83,33 +83,34 @@
 
 }  // namespace
 
-PaymentAppRequest PaymentAppRequestConversion::ToPaymentAppRequest(
+PaymentRequestEventInit
+PaymentRequestEventDataConversion::ToPaymentRequestEventInit(
     ScriptState* script_state,
-    const WebPaymentAppRequest& web_app_request) {
+    const WebPaymentRequestEventData& web_event_data) {
   DCHECK(script_state);
 
-  PaymentAppRequest app_request;
+  PaymentRequestEventInit event_data;
   if (!script_state->ContextIsValid())
-    return app_request;
+    return event_data;
 
   ScriptState::Scope scope(script_state);
 
-  app_request.setTopLevelOrigin(web_app_request.top_level_origin);
-  app_request.setPaymentRequestOrigin(web_app_request.payment_request_origin);
-  app_request.setPaymentRequestId(web_app_request.payment_request_id);
+  event_data.setTopLevelOrigin(web_event_data.top_level_origin);
+  event_data.setPaymentRequestOrigin(web_event_data.payment_request_origin);
+  event_data.setPaymentRequestId(web_event_data.payment_request_id);
   HeapVector<PaymentMethodData> method_data;
-  for (const auto& md : web_app_request.method_data) {
+  for (const auto& md : web_event_data.method_data) {
     method_data.push_back(ToPaymentMethodData(script_state, md));
   }
-  app_request.setMethodData(method_data);
-  app_request.setTotal(ToPaymentItem(web_app_request.total));
+  event_data.setMethodData(method_data);
+  event_data.setTotal(ToPaymentItem(web_event_data.total));
   HeapVector<PaymentDetailsModifier> modifiers;
-  for (const auto& modifier : web_app_request.modifiers) {
+  for (const auto& modifier : web_event_data.modifiers) {
     modifiers.push_back(ToPaymentDetailsModifier(script_state, modifier));
   }
-  app_request.setModifiers(modifiers);
-  app_request.setInstrumentKey(web_app_request.instrument_key);
-  return app_request;
+  event_data.setModifiers(modifiers);
+  event_data.setInstrumentKey(web_event_data.instrument_key);
+  return event_data;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.h b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.h
new file mode 100644
index 0000000..d142b2ff
--- /dev/null
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversion.h
@@ -0,0 +1,27 @@
+// 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 PaymentRequestEventDataConversion_h
+#define PaymentRequestEventDataConversion_h
+
+#include "modules/payments/PaymentRequestEventInit.h"
+#include "platform/wtf/Allocator.h"
+
+namespace blink {
+
+class ScriptState;
+struct WebPaymentRequestEventData;
+
+class MODULES_EXPORT PaymentRequestEventDataConversion {
+  STATIC_ONLY(PaymentRequestEventDataConversion);
+
+ public:
+  static PaymentRequestEventInit ToPaymentRequestEventInit(
+      ScriptState*,
+      const WebPaymentRequestEventData&);
+};
+
+}  // namespace blink
+
+#endif  // PaymentRequestEventDataConversion_h
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversionTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversionTest.cpp
similarity index 84%
rename from third_party/WebKit/Source/modules/payments/PaymentAppRequestConversionTest.cpp
rename to third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversionTest.cpp
index d7dae72..cfc81e74 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppRequestConversionTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEventDataConversionTest.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 "modules/payments/PaymentAppRequestConversion.h"
+#include "modules/payments/PaymentRequestEventDataConversion.h"
 
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/V8BindingForCore.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
 #include "platform/bindings/ScriptState.h"
-#include "public/platform/modules/payments/WebPaymentAppRequest.h"
+#include "public/platform/modules/payments/WebPaymentRequestEventData.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,8 +31,8 @@
   return web_method_data;
 }
 
-static WebPaymentAppRequest CreateWebPaymentAppRequestForTest() {
-  WebPaymentAppRequest web_data;
+static WebPaymentRequestEventData CreateWebPaymentRequestEventDataForTest() {
+  WebPaymentRequestEventData web_data;
   web_data.top_level_origin = WebString::FromUTF8("https://example.com");
   web_data.payment_request_origin = WebString::FromUTF8("https://example.com");
   web_data.payment_request_id = WebString::FromUTF8("payment-request-id");
@@ -44,11 +44,13 @@
   return web_data;
 }
 
-TEST(PaymentAppRequestConversionTest, ToPaymentAppRequest) {
+TEST(PaymentRequestEventDataConversionTest, ToPaymentRequestEventData) {
   V8TestingScope scope;
-  WebPaymentAppRequest web_data = CreateWebPaymentAppRequestForTest();
-  PaymentAppRequest data = PaymentAppRequestConversion::ToPaymentAppRequest(
-      scope.GetScriptState(), web_data);
+  WebPaymentRequestEventData web_data =
+      CreateWebPaymentRequestEventDataForTest();
+  PaymentRequestEventInit data =
+      PaymentRequestEventDataConversion::ToPaymentRequestEventInit(
+          scope.GetScriptState(), web_data);
 
   ASSERT_TRUE(data.hasTopLevelOrigin());
   EXPECT_EQ("https://example.com", data.topLevelOrigin());
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppRequest.idl b/third_party/WebKit/Source/modules/payments/PaymentRequestEventInit.idl
similarity index 82%
rename from third_party/WebKit/Source/modules/payments/PaymentAppRequest.idl
rename to third_party/WebKit/Source/modules/payments/PaymentRequestEventInit.idl
index 57cff55..8ad64f1 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppRequest.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEventInit.idl
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/webpayments-payment-apps-api/#idl-def-paymentapprequest
+// https://w3c.github.io/payment-handler/#paymentrequesteventinit-dictionary
 
 // TODO(zino): Once the spec issue is resolved, we should apply the changes.
 // Please see https://github.com/w3c/payment-handler/pull/162
-dictionary PaymentAppRequest {
+dictionary PaymentRequestEventInit {
     DOMString topLevelOrigin;
     DOMString paymentRequestOrigin;
     DOMString paymentRequestId;
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParamDescriptor.idl b/third_party/WebKit/Source/modules/webaudio/AudioParamDescriptor.idl
new file mode 100644
index 0000000..70cd045
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/AudioParamDescriptor.idl
@@ -0,0 +1,14 @@
+// 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.
+
+// See: https://webaudio.github.io/web-audio-api/#idl-def-AudioParamDescriptor
+dictionary AudioParamDescriptor {
+    required DOMString name;
+    float defaultValue = 0;
+
+    // TODO(hongchan): These numbers are minimum/maximum number possible for 
+    // |float| type. Remove this comment when the spec is fixed.
+    float minValue = -3.4028235e38;
+    float maxValue = 3.4028235e38;
+};
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
index 0afd125b..75fd1c8c 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
@@ -4,11 +4,15 @@
 
 #include "modules/webaudio/AudioWorkletGlobalScope.h"
 
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
 #include "bindings/core/v8/ToV8ForCore.h"
 #include "bindings/core/v8/V8BindingForCore.h"
 #include "bindings/core/v8/WorkerOrWorkletScriptController.h"
+#include "bindings/modules/v8/V8AudioParamDescriptor.h"
 #include "core/dom/ExceptionCode.h"
 #include "modules/webaudio/AudioBuffer.h"
+#include "modules/webaudio/AudioParamDescriptor.h"
 #include "modules/webaudio/AudioWorkletProcessor.h"
 #include "modules/webaudio/AudioWorkletProcessorDefinition.h"
 #include "platform/bindings/V8BindingMacros.h"
@@ -66,8 +70,7 @@
   }
 
   v8::Isolate* isolate = ScriptController()->GetScriptState()->GetIsolate();
-  v8::Local<v8::Context> context =
-      ScriptController()->GetScriptState()->GetContext();
+  v8::Local<v8::Context> context = ScriptController()->GetContext();
 
   if (!class_definition.V8Value()->IsFunction()) {
     exception_state.ThrowTypeError(
@@ -76,7 +79,7 @@
   }
 
   v8::Local<v8::Function> class_definition_local =
-      class_definition.V8Value().As<v8::Function>();
+      v8::Local<v8::Function>::Cast(class_definition.V8Value());
 
   v8::Local<v8::Value> prototype_value_local;
   bool prototype_extracted =
@@ -85,7 +88,7 @@
   DCHECK(prototype_extracted);
 
   v8::Local<v8::Object> prototype_object_local =
-      prototype_value_local.As<v8::Object>();
+      v8::Local<v8::Object>::Cast(prototype_value_local);
 
   v8::Local<v8::Value> process_value_local;
   bool process_extracted =
@@ -106,13 +109,36 @@
   }
 
   v8::Local<v8::Function> process_function_local =
-      process_value_local.As<v8::Function>();
+      v8::Local<v8::Function>::Cast(process_value_local);
 
+  // constructor() and process() functions are successfully parsed from the
+  // script code, thus create the definition. The rest of parsing process
+  // (i.e. parameterDescriptors) is optional.
   AudioWorkletProcessorDefinition* definition =
       AudioWorkletProcessorDefinition::Create(
           isolate, name, class_definition_local, process_function_local);
   DCHECK(definition);
 
+  v8::Local<v8::Value> parameter_descriptors_value_local;
+  bool did_get_parameter_descriptor =
+      class_definition_local->Get(context,
+                                  V8String(isolate, "parameterDescriptors"))
+          .ToLocal(&parameter_descriptors_value_local);
+
+  // If parameterDescriptor() is parsed and has a valid value, create a vector
+  // of |AudioParamDescriptor| and pass it to the definition.
+  if (did_get_parameter_descriptor &&
+      !parameter_descriptors_value_local->IsNullOrUndefined()) {
+    HeapVector<AudioParamDescriptor> audio_param_descriptors =
+        NativeValueTraits<IDLSequence<AudioParamDescriptor>>::NativeValue(
+            isolate, parameter_descriptors_value_local, exception_state);
+
+    if (exception_state.HadException())
+      return;
+
+    definition->SetAudioParamDescriptors(audio_param_descriptors);
+  }
+
   processor_definition_map_.Set(
       name,
       TraceWrapperMember<AudioWorkletProcessorDefinition>(this, definition));
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
index b6ce391..4a78437 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp
@@ -97,6 +97,19 @@
     waitable_event.Wait();
   }
 
+  void RunParsingParameterDescriptorTest(WorkerThread* thread) {
+    WaitableEvent waitable_event;
+    TaskRunnerHelper::Get(TaskType::kUnthrottled, thread)
+        ->PostTask(
+            BLINK_FROM_HERE,
+            CrossThreadBind(
+                &AudioWorkletGlobalScopeTest::
+                    RunParsingParameterDescriptorTestOnWorkletThread,
+                CrossThreadUnretained(this), CrossThreadUnretained(thread),
+                CrossThreadUnretained(&waitable_event)));
+    waitable_event.Wait();
+  }
+
  private:
   // Test if AudioWorkletGlobalScope and V8 components (ScriptState, Isolate)
   // are properly instantiated. Runs a simple processor registration and check
@@ -245,6 +258,53 @@
     wait_event->Signal();
   }
 
+  void RunParsingParameterDescriptorTestOnWorkletThread(
+      WorkerThread* thread,
+      WaitableEvent* wait_event) {
+    EXPECT_TRUE(thread->IsCurrentThread());
+
+    AudioWorkletGlobalScope* global_scope =
+        static_cast<AudioWorkletGlobalScope*>(thread->GlobalScope());
+    ScriptState* script_state =
+        global_scope->ScriptController()->GetScriptState();
+
+    ScriptState::Scope scope(script_state);
+
+    global_scope->ScriptController()->Evaluate(ScriptSourceCode(
+        R"JS(
+          registerProcessor('testProcessor', class {
+              static get parameterDescriptors () {
+                return [{
+                  name: 'gain',
+                  defaultValue: 0.707,
+                  minValue: 0.0,
+                  maxValue: 1.0
+                }];
+              }
+              constructor () {}
+              process () {}
+            }
+          )
+        )JS"));
+
+    AudioWorkletProcessorDefinition* definition =
+        global_scope->FindDefinition("testProcessor");
+    EXPECT_TRUE(definition);
+    EXPECT_EQ(definition->GetName(), "testProcessor");
+
+    const Vector<String> param_names =
+        definition->GetAudioParamDescriptorNames();
+    EXPECT_EQ(param_names[0], "gain");
+
+    const AudioParamDescriptor* descriptor =
+        definition->GetAudioParamDescriptor(param_names[0]);
+    EXPECT_EQ(descriptor->defaultValue(), 0.707f);
+    EXPECT_EQ(descriptor->minValue(), 0.0f);
+    EXPECT_EQ(descriptor->maxValue(), 1.0f);
+
+    wait_event->Signal();
+  }
+
   RefPtr<SecurityOrigin> security_origin_;
   std::unique_ptr<WorkerReportingProxy> reporting_proxy_;
 };
@@ -267,4 +327,10 @@
   thread->TerminateAndWait();
 }
 
+TEST_F(AudioWorkletGlobalScopeTest, ParsingParameterDescriptor) {
+  std::unique_ptr<AudioWorkletThread> thread = CreateAudioWorkletThread();
+  RunParsingParameterDescriptorTest(thread.get());
+  thread->TerminateAndWait();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.cpp
index e6a6d05..0593cb2 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.cpp
@@ -39,6 +39,30 @@
   return process_.NewLocal(isolate);
 }
 
+void AudioWorkletProcessorDefinition::SetAudioParamDescriptors(
+    const HeapVector<AudioParamDescriptor>& descriptors) {
+  audio_param_descriptors_ = descriptors;
+}
+
+const Vector<String>
+    AudioWorkletProcessorDefinition::GetAudioParamDescriptorNames() const {
+  Vector<String> names;
+  for (const auto& descriptor : audio_param_descriptors_) {
+    names.push_back(descriptor.name());
+  }
+  return names;
+}
+
+const AudioParamDescriptor*
+    AudioWorkletProcessorDefinition::GetAudioParamDescriptor (
+        const String& key) const {
+  for (const auto& descriptor : audio_param_descriptors_) {
+    if (descriptor.name() == key)
+      return &descriptor;
+  }
+  return nullptr;
+}
+
 DEFINE_TRACE_WRAPPERS(AudioWorkletProcessorDefinition) {
   visitor->TraceWrappers(constructor_.Cast<v8::Value>());
   visitor->TraceWrappers(process_.Cast<v8::Value>());
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
index 26e9f55c..bc08de3 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
@@ -6,6 +6,7 @@
 #define AudioWorkletProcessorDefinition_h
 
 #include "modules/ModulesExport.h"
+#include "modules/webaudio/AudioParamDescriptor.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/bindings/TraceWrapperV8Reference.h"
 #include "platform/heap/Handle.h"
@@ -36,15 +37,19 @@
   const String& GetName() const { return name_; }
   v8::Local<v8::Function> ConstructorLocal(v8::Isolate*);
   v8::Local<v8::Function> ProcessLocal(v8::Isolate*);
+  void SetAudioParamDescriptors(const HeapVector<AudioParamDescriptor>&);
+  const Vector<String> GetAudioParamDescriptorNames() const;
+  const AudioParamDescriptor* GetAudioParamDescriptor(const String& key) const;
 
-  DEFINE_INLINE_TRACE(){};
+  DEFINE_INLINE_TRACE() { visitor->Trace(audio_param_descriptors_); };
   DECLARE_TRACE_WRAPPERS();
 
  private:
-  AudioWorkletProcessorDefinition(v8::Isolate*,
-                                  const String& name,
-                                  v8::Local<v8::Function> constructor,
-                                  v8::Local<v8::Function> process);
+  AudioWorkletProcessorDefinition(
+      v8::Isolate*,
+      const String& name,
+      v8::Local<v8::Function> constructor,
+      v8::Local<v8::Function> process);
 
   const String name_;
 
@@ -53,8 +58,7 @@
   TraceWrapperV8Reference<v8::Function> constructor_;
   TraceWrapperV8Reference<v8::Function> process_;
 
-  // TODO(hongchan): A container for AudioParamDescriptor objects.
-  // ScopedPersistent<v8::Array> m_parameterDescriptors;
+  HeapVector<AudioParamDescriptor> audio_param_descriptors_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 8372497..c85bf8f 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -599,6 +599,10 @@
       name: "MediaCaptureFromVideo",
       status: "experimental",
     },
+    // Set to reflect the MediaCastOverlayButton feature.
+    {
+      name: "MediaCastOverlayButton",
+    },
     {
       name: "MediaControlsOverlayPlayButton",
       settable_from_internals: true,
diff --git a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
index 426d4c0..de2b29c 100644
--- a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
@@ -406,4 +406,8 @@
   RuntimeEnabledFeatures::setRemotePlaybackBackendEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableMediaCastOverlayButton(bool enable) {
+  RuntimeEnabledFeatures::setMediaCastOverlayButtonEnabled(enable);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorElementId.cpp b/third_party/WebKit/Source/platform/graphics/CompositorElementId.cpp
index 1ce0571..45c3d6690 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositorElementId.cpp
+++ b/third_party/WebKit/Source/platform/graphics/CompositorElementId.cpp
@@ -23,11 +23,14 @@
   return CompositorElementId(id);
 }
 
-CompositorElementId PLATFORM_EXPORT
-CompositorElementIdFromPaintLayerId(PaintLayerId id,
-                                    CompositorElementIdNamespace namespace_id) {
+CompositorElementId PLATFORM_EXPORT CompositorElementIdFromLayoutObjectId(
+    LayoutObjectId id,
+    CompositorElementIdNamespace namespace_id) {
   DCHECK(namespace_id == CompositorElementIdNamespace::kPrimary ||
-         namespace_id == CompositorElementIdNamespace::kScroll);
+         namespace_id == CompositorElementIdNamespace::kScroll ||
+         namespace_id == CompositorElementIdNamespace::kEffectFilter ||
+         namespace_id == CompositorElementIdNamespace::kEffectMask ||
+         namespace_id == CompositorElementIdNamespace::kScrollTranslation);
   return CreateCompositorElementId(id, namespace_id);
 }
 
@@ -51,6 +54,11 @@
   return CreateCompositorElementId(id, namespace_id);
 }
 
+CompositorElementId CompositorElementIdFromRootEffectId(uint64_t id) {
+  return CreateCompositorElementId(id,
+                                   CompositorElementIdNamespace::kEffectRoot);
+}
+
 CompositorElementIdNamespace NamespaceFromCompositorElementId(
     CompositorElementId element_id) {
   return static_cast<CompositorElementIdNamespace>(
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorElementId.h b/third_party/WebKit/Source/platform/graphics/CompositorElementId.h
index 42b99f0..9942ab4 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositorElementId.h
+++ b/third_party/WebKit/Source/platform/graphics/CompositorElementId.h
@@ -13,7 +13,7 @@
 
 namespace blink {
 
-const int kCompositorNamespaceBitCount = 3;
+const int kCompositorNamespaceBitCount = 4;
 
 enum class CompositorElementIdNamespace {
   kPrimary,
@@ -25,26 +25,31 @@
   kLinkHighlight,
   kPrimaryCompositorProxy,
   kScrollCompositorProxy,
+  // The following are SPv2-only.
+  kEffectFilter,
+  kEffectMask,
+  kEffectRoot,
+  kScrollTranslation,
   // A sentinel to indicate the maximum representable namespace id
   // (the maximum is one less than this value).
   kMaxRepresentableNamespaceId = 1 << kCompositorNamespaceBitCount
 };
 
 using CompositorElementId = cc::ElementId;
-using PaintLayerId = uint64_t;
+using LayoutObjectId = uint64_t;
 using ScrollbarId = uint64_t;
 using DOMNodeId = uint64_t;
 
 CompositorElementId PLATFORM_EXPORT
-    CompositorElementIdFromPaintLayerId(PaintLayerId,
-                                        CompositorElementIdNamespace);
+    CompositorElementIdFromLayoutObjectId(LayoutObjectId,
+                                          CompositorElementIdNamespace);
 
 // This method should only be used for "special" layers that are not allocated
 // during the normal lifecycle. Examples include VisualViewport,
 // root scrolling (when rootLayerScrollingEnabled is off), and LinkHighlight,
 // or when CompositorProxies are involved.
 
-// Otherwise, CompositorElementIdFromPaintLayerId is preferred for performance
+// Otherwise, CompositorElementIdFromLayoutObjectId is preferred for performance
 // reasons (since computing a DOMNodeId requires a hash map lookup),
 // and future compatibility with multicol/pagination.
 CompositorElementId PLATFORM_EXPORT
@@ -54,6 +59,9 @@
     CompositorElementIdFromScrollbarId(ScrollbarId,
                                        CompositorElementIdNamespace);
 
+CompositorElementId PLATFORM_EXPORT
+CompositorElementIdFromRootEffectId(uint64_t id);
+
 // Note cc::ElementId has a hash function already implemented via
 // ElementIdHash::operator(). However for consistency's sake we choose to use
 // Blink's hash functions with Blink specific data structures.
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorElementIdTest.cpp b/third_party/WebKit/Source/platform/graphics/CompositorElementIdTest.cpp
index 50a5c61..ad80575 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositorElementIdTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/CompositorElementIdTest.cpp
@@ -15,7 +15,7 @@
 }
 
 TEST_F(CompositorElementIdTest, EncodeDecode) {
-  CompositorElementId element_id = CompositorElementIdFromPaintLayerId(
+  CompositorElementId element_id = CompositorElementIdFromLayoutObjectId(
       1, CompositorElementIdNamespace::kPrimary);
   EXPECT_EQ(1u, IdFromCompositorElementId(element_id));
   EXPECT_EQ(CompositorElementIdNamespace::kPrimary,
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index a3e3182..60749c7 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -647,7 +647,7 @@
       EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
       ClipPaintPropertyNode::Root(), kColorFilterNone,
       CompositorFilterOperations(), 0.5, SkBlendMode::kSrcOver,
-      kCompositingReasonAll);
+      kCompositingReasonAll, CompositorElementId(2));
   RefPtr<EffectPaintPropertyNode> effect2 = EffectPaintPropertyNode::Create(
       effect1, TransformPaintPropertyNode::Root(),
       ClipPaintPropertyNode::Root(), kColorFilterNone,
@@ -684,10 +684,13 @@
 
   const cc::EffectNode& converted_root_effect = *effect_tree.Node(1);
   EXPECT_EQ(-1, converted_root_effect.parent_id);
+  EXPECT_EQ(CompositorElementIdFromRootEffectId(1).id_,
+            converted_root_effect.stable_id);
 
   const cc::EffectNode& converted_effect1 = *effect_tree.Node(2);
   EXPECT_EQ(converted_root_effect.id, converted_effect1.parent_id);
   EXPECT_FLOAT_EQ(0.5, converted_effect1.opacity);
+  EXPECT_EQ(2u, converted_effect1.stable_id);
 
   const cc::EffectNode& converted_effect2 = *effect_tree.Node(3);
   EXPECT_EQ(converted_effect1.id, converted_effect2.parent_id);
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
index 9ad9dd7..4f91a4ba 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
@@ -126,11 +126,12 @@
   cc::EffectNode& effect_node =
       *effect_tree.Node(effect_tree.Insert(cc::EffectNode(), kInvalidNodeId));
   DCHECK_EQ(effect_node.id, kSecondaryRootNodeId);
-  effect_node.stable_id = root_layer_->id();
+  effect_node.stable_id =
+      CompositorElementIdFromRootEffectId(kSecondaryRootNodeId).id_;
   effect_node.transform_id = kRealRootNodeId;
   effect_node.clip_id = kSecondaryRootNodeId;
   effect_node.has_render_surface = true;
-  effect_tree.SetOwningLayerIdForNode(&effect_node, effect_node.stable_id);
+  effect_tree.SetOwningLayerIdForNode(&effect_node, kSecondaryRootNodeId);
 
   effect_stack_.push_back(
       BlinkEffectAndCcIdPair{EffectPaintPropertyNode::Root(), effect_node.id});
@@ -416,7 +417,7 @@
 
   cc::EffectNode& effect_node = *GetEffectTree().Node(GetEffectTree().Insert(
       cc::EffectNode(), GetCurrentCompositorEffectNodeIndex()));
-  effect_node.stable_id = dummy_layer->id();
+  effect_node.stable_id = next_effect->GetCompositorElementId().id_;
   effect_node.clip_id = output_clip_id;
   // Every effect is supposed to have render surface enabled for grouping,
   // but we can get away without one if the effect is opacity-only and has only
@@ -448,6 +449,9 @@
   CompositorElementId compositor_element_id =
       next_effect->GetCompositorElementId();
   if (compositor_element_id) {
+    DCHECK(property_trees_.element_id_to_effect_node_index.find(
+               compositor_element_id) ==
+           property_trees_.element_id_to_effect_node_index.end());
     property_trees_.element_id_to_effect_node_index[compositor_element_id] =
         effect_node.id;
   }
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
index 7308dcfa..6192157 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
@@ -54,9 +54,9 @@
 #include "modules/notifications/Notification.h"
 #include "modules/notifications/NotificationEvent.h"
 #include "modules/notifications/NotificationEventInit.h"
-#include "modules/payments/PaymentAppRequest.h"
-#include "modules/payments/PaymentAppRequestConversion.h"
 #include "modules/payments/PaymentRequestEvent.h"
+#include "modules/payments/PaymentRequestEventDataConversion.h"
+#include "modules/payments/PaymentRequestEventInit.h"
 #include "modules/payments/PaymentRequestRespondWithObserver.h"
 #include "modules/push_messaging/PushEvent.h"
 #include "modules/push_messaging/PushMessageData.h"
@@ -453,7 +453,7 @@
 
 void ServiceWorkerGlobalScopeProxy::DispatchPaymentRequestEvent(
     int event_id,
-    const WebPaymentAppRequest& web_app_request) {
+    const WebPaymentRequestEventData& web_app_request) {
   WaitUntilObserver* wait_until_observer = WaitUntilObserver::Create(
       WorkerGlobalScope(), WaitUntilObserver::kPaymentRequest, event_id);
   RespondWithObserver* respond_with_observer =
@@ -462,7 +462,7 @@
 
   Event* event = PaymentRequestEvent::Create(
       EventTypeNames::paymentrequest,
-      PaymentAppRequestConversion::ToPaymentAppRequest(
+      PaymentRequestEventDataConversion::ToPaymentRequestEventInit(
           WorkerGlobalScope()->ScriptController()->GetScriptState(),
           web_app_request),
       respond_with_observer, wait_until_observer);
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
index 6eee311..15cad682 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
@@ -119,7 +119,8 @@
                                       const WebNotificationData&) override;
   void DispatchPushEvent(int, const WebString& data) override;
   void DispatchSyncEvent(int, const WebString& tag, LastChanceOption) override;
-  void DispatchPaymentRequestEvent(int, const WebPaymentAppRequest&) override;
+  void DispatchPaymentRequestEvent(int,
+                                   const WebPaymentRequestEventData&) override;
   bool HasFetchEventHandler() override;
   void OnNavigationPreloadResponse(
       int fetch_event_id,
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
index a72b4e1..9ba29f0 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -147,10 +147,6 @@
   NOTREACHED();
 }
 
-void WebRemoteFrameImpl::ExecuteScript(const WebScriptSource&) {
-  NOTREACHED();
-}
-
 void WebRemoteFrameImpl::ExecuteScriptInIsolatedWorld(
     int world_id,
     const WebScriptSource* sources,
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
index b8dbf1d..f9883a5 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
@@ -46,7 +46,6 @@
   WebDocument GetDocument() const override;
   WebPerformance Performance() const override;
   void DispatchUnloadEvent() override;
-  void ExecuteScript(const WebScriptSource&) override;
   void ExecuteScriptInIsolatedWorld(int world_id,
                                     const WebScriptSource* sources,
                                     unsigned num_sources) override;
diff --git a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
index 648369bf..8eaf3cb 100644
--- a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
+++ b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
@@ -211,7 +211,7 @@
   }
 
   void ExecuteScript(const char* code) {
-    web_view_helper_.WebView()->MainFrame()->ExecuteScript(
+    web_view_helper_.WebView()->MainFrameImpl()->ExecuteScript(
         WebScriptSource(WebString::FromUTF8(code)));
   }
 
diff --git a/third_party/WebKit/Source/web/tests/ViewportTest.cpp b/third_party/WebKit/Source/web/tests/ViewportTest.cpp
index 842790f..03058ca 100644
--- a/third_party/WebKit/Source/web/tests/ViewportTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ViewportTest.cpp
@@ -32,6 +32,7 @@
 #include "core/frame/LocalFrameView.h"
 #include "core/frame/PageScaleConstraints.h"
 #include "core/frame/Settings.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "core/page/Page.h"
 #include "platform/Length.h"
 #include "platform/geometry/IntPoint.h"
@@ -44,6 +45,7 @@
 #include "public/platform/WebURLLoaderMockFactory.h"
 #include "public/web/WebConsoleMessage.h"
 #include "public/web/WebFrame.h"
+#include "public/web/WebLocalFrame.h"
 #include "public/web/WebScriptSource.h"
 #include "public/web/WebSettings.h"
 #include "public/web/WebViewClient.h"
@@ -77,7 +79,7 @@
         WebString::FromUTF8(file_name));
   }
 
-  void ExecuteScript(WebFrame* frame, const WebString& code) {
+  void ExecuteScript(WebLocalFrame* frame, const WebString& code) {
     frame->ExecuteScript(WebScriptSource(code));
     RunPendingTasks();
   }
@@ -2880,7 +2882,7 @@
   EXPECT_NEAR(5.0f, constraints.maximum_scale, 0.01f);
   EXPECT_TRUE(page->GetViewportDescription().user_zoom);
 
-  ExecuteScript(web_view_helper.WebView()->MainFrame(),
+  ExecuteScript(web_view_helper.WebView()->MainFrameImpl(),
                 "originalDoctype = document.doctype;"
                 "document.removeChild(originalDoctype);");
 
@@ -2893,7 +2895,7 @@
   EXPECT_NEAR(5.0f, constraints.maximum_scale, 0.01f);
   EXPECT_TRUE(page->GetViewportDescription().user_zoom);
 
-  ExecuteScript(web_view_helper.WebView()->MainFrame(),
+  ExecuteScript(web_view_helper.WebView()->MainFrameImpl(),
                 "document.insertBefore(originalDoctype, document.firstChild);");
 
   constraints = RunViewportTest(page, 320, 352);
diff --git a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
index 951583e..ccbba39 100644
--- a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
+++ b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
@@ -1012,13 +1012,13 @@
   WebRect extent_rect;
 
   WebViewImpl()->SetPageScaleFactor(2);
-  WebFrame* mainFrame = WebViewImpl()->MainFrame();
+  WebLocalFrame* mainFrame = WebViewImpl()->MainFrameImpl();
 
   // Select some text and get the base and extent rects (that's the start of
   // the range and its end). Do a sanity check that the expected text is
   // selected
   mainFrame->ExecuteScript(WebScriptSource("selectRange();"));
-  EXPECT_EQ("ir", mainFrame->ToWebLocalFrame()->SelectionAsText().Utf8());
+  EXPECT_EQ("ir", mainFrame->SelectionAsText().Utf8());
 
   WebViewImpl()->SelectionBounds(base_rect, extent_rect);
   WebPoint initialPoint(base_rect.x, base_rect.y);
@@ -1029,8 +1029,8 @@
   // right and down one line.
   VisualViewport& visual_viewport = GetFrame()->GetPage()->GetVisualViewport();
   visual_viewport.Move(ScrollOffset(60, 25));
-  mainFrame->ToWebLocalFrame()->MoveRangeSelection(initialPoint, endPoint);
-  EXPECT_EQ("t ", mainFrame->ToWebLocalFrame()->SelectionAsText().Utf8());
+  mainFrame->MoveRangeSelection(initialPoint, endPoint);
+  EXPECT_EQ("t ", mainFrame->SelectionAsText().Utf8());
 }
 
 // Test that the scrollFocusedEditableElementIntoRect method works with the
@@ -1635,7 +1635,7 @@
 
   LocalFrameView& frame_view = *WebViewImpl()->MainFrameImpl()->GetFrameView();
 
-  WebViewImpl()->MainFrame()->ExecuteScript(
+  WebViewImpl()->MainFrameImpl()->ExecuteScript(
       WebScriptSource("var content = document.getElementById(\"content\");"
                       "content.style.width = \"1500px\";"
                       "content.style.height = \"2400px\";"));
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 0e7d46f..075bd61 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -453,8 +453,7 @@
   ScriptExecutionCallbackHelper callback_helper(
       web_view_helper.WebView()->MainFrame()->MainWorldScriptContext());
   web_view_helper.WebView()
-      ->MainFrame()
-      ->ToWebLocalFrame()
+      ->MainFrameImpl()
       ->RequestExecuteScriptAndReturnValue(
           WebScriptSource(WebString("'hello';")), false, &callback_helper);
   RunPendingTasks();
@@ -4221,8 +4220,7 @@
   web_view_helper.InitializeAndLoad(base_url_ + "textbox.html", true);
   web_view_helper.Resize(WebSize(640, 480));
 
-  WebLocalFrame* main_frame =
-      web_view_helper.WebView()->MainFrame()->ToWebLocalFrame();
+  WebLocalFrame* main_frame = web_view_helper.WebView()->MainFrameImpl();
   main_frame->ExecuteScript(WebScriptSource("selectRange();"));
 
   WebRect old_rect;
@@ -4677,7 +4675,7 @@
   int isolated_world_id = 42;
   WebScriptSource script_source("hi!");
   int num_sources = 1;
-  web_view_helper.WebView()->MainFrame()->ExecuteScriptInIsolatedWorld(
+  web_view_helper.WebView()->MainFrameImpl()->ExecuteScriptInIsolatedWorld(
       isolated_world_id, &script_source, num_sources);
 
   // We should now have a new create notification.
@@ -6968,13 +6966,13 @@
 
   // Create another window that will try to access it.
   FrameTestHelpers::WebViewHelper new_web_view_helper;
-  WebView* new_view = new_web_view_helper.InitializeWithOpener(
+  WebViewBase* new_view = new_web_view_helper.InitializeWithOpener(
       web_view_helper.WebView()->MainFrame(), true);
   RunPendingTasks();
   EXPECT_EQ(0, web_frame_client.did_access_initial_document_);
 
   // Access the initial document by modifying the body.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.document.body.innerHTML += 'Modified';"));
   RunPendingTasks();
   EXPECT_EQ(2, web_frame_client.did_access_initial_document_);
@@ -6991,14 +6989,14 @@
 
   // Create another window that will try to access it.
   FrameTestHelpers::WebViewHelper new_web_view_helper;
-  WebView* new_view = new_web_view_helper.InitializeWithOpener(
+  WebViewBase* new_view = new_web_view_helper.InitializeWithOpener(
       web_view_helper.WebView()->MainFrame(), true);
   RunPendingTasks();
   EXPECT_EQ(0, web_frame_client.did_access_initial_document_);
 
   // Access the initial document by calling document.open(), which allows
   // arbitrary modification of the initial document.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.document.open();"));
   RunPendingTasks();
   EXPECT_EQ(1, web_frame_client.did_access_initial_document_);
@@ -7015,13 +7013,13 @@
 
   // Create another window that will try to access it.
   FrameTestHelpers::WebViewHelper new_web_view_helper;
-  WebView* new_view = new_web_view_helper.InitializeWithOpener(
+  WebViewBase* new_view = new_web_view_helper.InitializeWithOpener(
       web_view_helper.WebView()->MainFrame(), true);
   RunPendingTasks();
   EXPECT_EQ(0, web_frame_client.did_access_initial_document_);
 
   // Access the initial document to get to the navigator object.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("console.log(window.opener.navigator);"));
   RunPendingTasks();
   EXPECT_EQ(3, web_frame_client.did_access_initial_document_);
@@ -7055,19 +7053,19 @@
 
   // Create another window that will try to access it.
   FrameTestHelpers::WebViewHelper new_web_view_helper;
-  WebView* new_view = new_web_view_helper.InitializeWithOpener(
+  WebViewBase* new_view = new_web_view_helper.InitializeWithOpener(
       web_view_helper.WebView()->MainFrame(), true);
   RunPendingTasks();
   EXPECT_EQ(0, web_frame_client.did_access_initial_document_);
 
   // Access the initial document by modifying the body.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.document.body.innerHTML += 'Modified';"));
   EXPECT_EQ(2, web_frame_client.did_access_initial_document_);
 
   // Run a modal dialog, which used to run a nested run loop and require
   // a special case for notifying about the access.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.confirm('Modal');"));
   EXPECT_EQ(3, web_frame_client.did_access_initial_document_);
 
@@ -7088,21 +7086,21 @@
 
   // Create another window that will try to access it.
   FrameTestHelpers::WebViewHelper new_web_view_helper;
-  WebView* new_view = new_web_view_helper.InitializeWithOpener(
+  WebViewBase* new_view = new_web_view_helper.InitializeWithOpener(
       web_view_helper.WebView()->MainFrame(), true);
   RunPendingTasks();
   EXPECT_EQ(0, web_frame_client.did_access_initial_document_);
 
   // Access the initial document with document.write, which moves us past the
   // initial empty document state of the state machine.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.document.write('Modified'); "
                       "window.opener.document.close();"));
   EXPECT_EQ(1, web_frame_client.did_access_initial_document_);
 
   // Run a modal dialog, which used to run a nested run loop and require
   // a special case for notifying about the access.
-  new_view->MainFrame()->ExecuteScript(
+  new_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("window.opener.confirm('Modal');"));
   EXPECT_EQ(1, web_frame_client.did_access_initial_document_);
 
@@ -7763,7 +7761,7 @@
   FrameTestHelpers::WebViewHelper web_view_helper;
   web_view_helper.InitializeAndLoad("about:blank", true, &client);
 
-  WebFrame* frame = web_view_helper.WebView()->MainFrame();
+  WebLocalFrame* frame = web_view_helper.WebView()->MainFrameImpl();
 
   frame->ExecuteScript(WebScriptSource(WebString::FromUTF8(
       "document.body.appendChild(document.createElement('iframe'))")));
@@ -7837,8 +7835,7 @@
   ASSERT_FALSE(web_scroll_layer->UserScrollableVertical());
 
   // Call javascript to make the layer scrollable, and verify it.
-  WebLocalFrameBase* frame =
-      (WebLocalFrameBase*)web_view_helper.WebView()->MainFrame();
+  WebLocalFrameBase* frame = web_view_helper.WebView()->MainFrameImpl();
   frame->ExecuteScript(WebScriptSource("allowScroll();"));
   web_view_helper.WebView()->UpdateAllLifecyclePhases();
   ASSERT_TRUE(web_scroll_layer->UserScrollableHorizontal());
@@ -8898,8 +8895,8 @@
   }
 
   void Reset() { web_view_helper_.Reset(); }
-  WebFrame* MainFrame() const {
-    return web_view_helper_.WebView()->MainFrame();
+  WebLocalFrame* MainFrame() const {
+    return web_view_helper_.WebView()->MainFrameImpl();
   }
   WebViewBase* WebView() const { return web_view_helper_.WebView(); }
 
@@ -9560,7 +9557,6 @@
   remote_frame->SetReplicatedOrigin(
       WebSecurityOrigin::CreateFromString("http://127.0.0.1"));
 
-  ASSERT_TRUE(MainFrame()->IsWebLocalFrame());
   ASSERT_TRUE(MainFrame()->FirstChild()->IsWebRemoteFrame());
   LocalDOMWindow* main_window =
       ToWebLocalFrameBase(MainFrame())->GetFrame()->DomWindow();
@@ -9842,14 +9838,14 @@
   // first window.
   FrameTestHelpers::WebViewHelper popup_web_view_helper;
   TestConsoleMessageWebFrameClient popup_web_frame_client;
-  WebView* popup_view = popup_web_view_helper.InitializeAndLoad(
+  WebViewBase* popup_view = popup_web_view_helper.InitializeAndLoad(
       chrome_url_ + "hello_world.html", true, &popup_web_frame_client);
   popup_view->MainFrame()->SetOpener(web_view_helper.WebView()->MainFrame());
 
   // Attempt a blocked navigation of an opener's subframe, and ensure that
   // the error shows up on the popup (calling) window's console, rather than
   // the target window.
-  popup_view->MainFrame()->ExecuteScript(WebScriptSource(
+  popup_view->MainFrameImpl()->ExecuteScript(WebScriptSource(
       "try { opener.frames[1].location.href='data:text/html,foo'; } catch (e) "
       "{}"));
   EXPECT_TRUE(web_frame_client.messages.IsEmpty());
@@ -9860,7 +9856,7 @@
 
   // Try setting a cross-origin iframe element's source to a javascript: URL,
   // and check that this error is also printed on the calling window.
-  popup_view->MainFrame()->ExecuteScript(
+  popup_view->MainFrameImpl()->ExecuteScript(
       WebScriptSource("opener.document.querySelectorAll('iframe')[1].src='"
                       "javascript:alert()'"));
   EXPECT_TRUE(web_frame_client.messages.IsEmpty());
@@ -10551,7 +10547,7 @@
     RegisterMockedHttpURLLoad("single_iframe.html");
     frame_ = web_view_helper_
                  .InitializeAndLoad(base_url_ + "single_iframe.html", true)
-                 ->MainFrame();
+                 ->MainFrameImpl();
     web_remote_frame_ = RemoteFrameClient()->GetFrame();
   }
 
@@ -10568,7 +10564,7 @@
     RemoteFrame()->SetReplicatedOrigin(SecurityOrigin::CreateUnique());
   }
 
-  WebFrame* MainFrame() { return frame_; }
+  WebLocalFrame* MainFrame() { return frame_; }
   WebRemoteFrameImpl* RemoteFrame() { return web_remote_frame_; }
   TestWebRemoteFrameClientForVisibility* RemoteFrameClient() {
     return &remote_frame_client_;
@@ -10577,7 +10573,7 @@
  private:
   TestWebRemoteFrameClientForVisibility remote_frame_client_;
   FrameTestHelpers::WebViewHelper web_view_helper_;
-  WebFrame* frame_;
+  WebLocalFrame* frame_;
   Persistent<WebRemoteFrameImpl> web_remote_frame_;
 };
 
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 0c0b7152..14c2434 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -3960,7 +3960,7 @@
   WebViewBase* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "single_iframe.html", true);
 
-  WebFrame* frame = web_view_helper_.WebView()->MainFrame();
+  WebLocalFrame* frame = web_view_helper_.WebView()->MainFrameImpl();
   Document* document =
       ToLocalFrame(web_view_helper_.WebView()->GetPage()->MainFrame())
           ->GetDocument();
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index dbcceb8..f2aacc44 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -388,12 +388,12 @@
     "platform/modules/notifications/WebNotificationDelegate.h",
     "platform/modules/notifications/WebNotificationManager.h",
     "platform/modules/notifications/WebNotificationResources.h",
-    "platform/modules/payments/WebPaymentAppRequest.h",
     "platform/modules/payments/WebPaymentAppResponse.h",
     "platform/modules/payments/WebPaymentCurrencyAmount.h",
     "platform/modules/payments/WebPaymentDetailsModifier.h",
     "platform/modules/payments/WebPaymentItem.h",
     "platform/modules/payments/WebPaymentMethodData.h",
+    "platform/modules/payments/WebPaymentRequestEventData.h",
     "platform/modules/presentation/WebPresentationAvailabilityObserver.h",
     "platform/modules/presentation/WebPresentationClient.h",
     "platform/modules/presentation/WebPresentationConnection.h",
diff --git a/third_party/WebKit/public/platform/WebMediaPlayer.h b/third_party/WebKit/public/platform/WebMediaPlayer.h
index aa08930..4920bad 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayer.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayer.h
@@ -282,6 +282,13 @@
                                         double* timestamp) {
     return false;
   }
+
+  // Callback called whenever the media element may have received or last native
+  // controls. It might be called twice with the same value: the caller has to
+  // check if the value have changed if it only wants to handle this case.
+  // This method is not used to say express if the native controls are visible
+  // but if the element is using them.
+  virtual void OnHasNativeControlsChanged(bool) {}
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/WebMediaPlayerClient.h b/third_party/WebKit/public/platform/WebMediaPlayerClient.h
index 986e0a3..ac143a51 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayerClient.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayerClient.h
@@ -117,6 +117,10 @@
   virtual void MediaRemotingStarted() {}
   virtual void MediaRemotingStopped() {}
 
+  // Returns whether the media element has native controls. It does not mean
+  // that the controls are currently visible.
+  virtual bool HasNativeControls() = 0;
+
  protected:
   ~WebMediaPlayerClient() {}
 };
diff --git a/third_party/WebKit/public/platform/WebRuntimeFeatures.h b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
index 48783be9f..db911e7d 100644
--- a/third_party/WebKit/public/platform/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
@@ -157,6 +157,7 @@
   BLINK_PLATFORM_EXPORT static void EnableMediaControlsOverlayPlayButton(bool);
   BLINK_PLATFORM_EXPORT static void EnableLocationHardReload(bool);
   BLINK_PLATFORM_EXPORT static void EnableRemotePlaybackBackend(bool);
+  BLINK_PLATFORM_EXPORT static void EnableMediaCastOverlayButton(bool);
 
  private:
   WebRuntimeFeatures();
diff --git a/third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h b/third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.h
similarity index 77%
rename from third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h
rename to third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.h
index 2a98a8c..5b0ae340 100644
--- a/third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h
+++ b/third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.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 WebPaymentAppRequest_h
-#define WebPaymentAppRequest_h
+#ifndef WebPaymentRequestEventData_h
+#define WebPaymentRequestEventData_h
 
 #include "public/platform/WebString.h"
 #include "public/platform/WebVector.h"
@@ -13,8 +13,7 @@
 
 namespace blink {
 
-// https://w3c.github.io/webpayments-payment-apps-api/#idl-def-paymentapprequest
-struct WebPaymentAppRequest {
+struct WebPaymentRequestEventData {
   WebString top_level_origin;
   WebString payment_request_origin;
   WebString payment_request_id;
@@ -26,4 +25,4 @@
 
 }  // namespace blink
 
-#endif  // WebPaymentAppRequest_h
+#endif  // WebPaymentRequestEventData_h
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 434a862214..47e76e1 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -224,9 +224,6 @@
 
   // Scripting ----------------------------------------------------------
 
-  // Executes script in the context of the current page.
-  virtual void ExecuteScript(const WebScriptSource&) = 0;
-
   // Executes JavaScript in a new world associated with the web frame.
   // The script gets its own global scope and its own prototypes for
   // intrinsic JavaScript objects (String, Array, and so-on). It also
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index ead2f68..0e7ac50 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -266,6 +266,10 @@
                                  int page_index) = 0;
 
   // Scripting --------------------------------------------------------------
+
+  // Executes script in the context of the current page.
+  virtual void ExecuteScript(const WebScriptSource&) = 0;
+
   // Executes script in the context of the current page and returns the value
   // that the script evaluated to with callback. Script execution can be
   // suspend.
@@ -568,6 +572,7 @@
   virtual WebInputMethodController* GetInputMethodController() = 0;
 
   // Loading ------------------------------------------------------------------
+
   // Creates and returns a loader. This function can be called only when this
   // frame is attached to a document.
   virtual std::unique_ptr<WebURLLoader> CreateURLLoader() = 0;
diff --git a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h
index dbf6378..d3487da 100644
--- a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h
+++ b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h
@@ -44,7 +44,7 @@
 class WebServiceWorkerRequest;
 class WebString;
 struct WebNotificationData;
-struct WebPaymentAppRequest;
+struct WebPaymentRequestEventData;
 struct WebServiceWorkerClientInfo;
 struct WebServiceWorkerError;
 class WebURLResponse;
@@ -115,8 +115,9 @@
                                  const WebString& tag,
                                  LastChanceOption) = 0;
 
-  virtual void DispatchPaymentRequestEvent(int event_id,
-                                           const WebPaymentAppRequest&) = 0;
+  virtual void DispatchPaymentRequestEvent(
+      int event_id,
+      const WebPaymentRequestEventData&) = 0;
 
   virtual void OnNavigationPreloadResponse(
       int fetch_event_id,
diff --git a/tools/chrome_proxy/webdriver/lite_page.py b/tools/chrome_proxy/webdriver/lite_page.py
index 39b6d43e..f758acf 100644
--- a/tools/chrome_proxy/webdriver/lite_page.py
+++ b/tools/chrome_proxy/webdriver/lite_page.py
@@ -50,6 +50,37 @@
       # Verify that a Lite Page response for the main frame was seen.
       self.assertEqual(1, lite_page_responses)
 
+
+  # Checks that a Lite Page is not served for the Cellular-Only option but
+  # not on cellular connection.
+  def testLitePageNotAcceptedForCellularOnlyFlag(self):
+    with TestDriver() as test_driver:
+      test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+      test_driver.AddChromeArg('--data-reduction-proxy-lo-fi=cellular-only')
+      test_driver.AddChromeArg('--enable-data-reduction-proxy-lite-page')
+
+      test_driver.LoadURL('http://check.googlezip.net/test.html')
+
+      non_lite_page_responses = 0
+      for response in test_driver.GetHTTPResponses():
+        if response.url.endswith('html'):
+          self.assertNotIn('chrome-proxy-accept-transform',
+                           response.request_headers)
+          self.assertNotIn('chrome-proxy-content-transform',
+                           response.response_headers)
+          non_lite_page_responses = non_lite_page_responses + 1
+          # Note that the client will still send exp=force_lite_page (if not
+          # using the exp paramter to specify other experiments).
+          if common.ParseFlags().browser_args:
+            if ('--data-reduction-proxy-experiment'
+                not in common.ParseFlags().browser_args):
+              # Verify force directive present.
+              self.assertIn('exp=force_lite_page',
+                response.request_headers['chrome-proxy'])
+
+      # Verify that a main frame without Lite Page was seen.
+      self.assertEqual(1, non_lite_page_responses)
+
   # Checks that a Lite Page does not have an error when scrolling to the bottom
   # of the page and is able to load all resources.
   def testLitePageBTF(self):
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 93369d1..283764b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -30432,6 +30432,14 @@
   </summary>
 </histogram>
 
+<histogram name="MediaRouter.Ui.Dialog.LoadedWebUiRouteController" units="ms">
+  <owner>takumif@chromium.org</owner>
+  <summary>
+    Duration in milliseconds it takes the WebUI route controls in the route
+    details view to be populated after the view is opened.
+  </summary>
+</histogram>
+
 <histogram name="MediaRouter.Ui.Dialog.LoadedWithData" units="ms">
   <owner>apacible@chromium.org</owner>
   <summary>
@@ -80879,6 +80887,11 @@
   </summary>
 </histogram>
 
+<histogram name="VRRuntimeVersion" units="normalized version">
+  <owner>tiborg@chromium.org</owner>
+  <summary>The version of the runtime being used for VR.</summary>
+</histogram>
+
 <histogram name="VRSessionNavigationCount">
   <owner>billorr@chromium.org</owner>
   <summary>
@@ -89175,6 +89188,12 @@
              power."/>
   <suffix name="Audio.MSE"
       label="Watch time for MSE media with only an audio track."/>
+  <suffix name="Audio.NativeControlsOff"
+      label="Watch time for all media with only an audio track not using
+             native controls."/>
+  <suffix name="Audio.NativeControlsOn"
+      label="Watch time for all media with only an audio track using native
+             controls."/>
   <suffix name="Audio.SRC"
       label="Watch time for SRC media with only an audio track."/>
   <suffix name="Audio.EME"
@@ -89192,6 +89211,12 @@
              battery power."/>
   <suffix name="AudioVideo.MSE"
       label="Watch time for MSE media with both an audio and video track."/>
+  <suffix name="AudioVideo.NativeControlsOff"
+      label="Watch time for all media with both an audio and video track not
+             using native controls."/>
+  <suffix name="AudioVideo.NativeControlsOn"
+      label="Watch time for all media with both an audio and video track
+             using native controls."/>
   <suffix name="AudioVideo.SRC"
       label="Watch time for SRC media with both an audio and video track."/>
   <suffix name="AudioVideo.EME"
@@ -94911,6 +94936,22 @@
   <affected-histogram name="WebRTC.Video.Encoded.Qp"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="VRRuntimeVersionGVRHeadset" separator=".">
+  <suffix name="Cardboard" label="GVR version used with Carboard."/>
+  <suffix name="Daydream" label="GVR version used with Daydream."/>
+  <suffix name="Unknown" label="GVR version used with an unknown headset."/>
+  <affected-histogram name="VRRuntimeVersion.GVR"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="VRRuntimeVersionPlatform" separator=".">
+  <suffix name="GVR"
+      label="The GVR version being used for VR. Special values: (-4) Could
+             not encode GVR version; (-3) Device does not support VR; (-2)
+             GVR is not installed; (-1) GVR is installed but version is too
+             old to be logged."/>
+  <affected-histogram name="VRRuntimeVersion"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="VRSessionType" separator=".">
   <suffix name="Browser"
       label="The session is restricted to the period that the browser is
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 9e90e2042..ebbf02f 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -1196,6 +1196,22 @@
   </summary>
 </rappor-metric>
 
+<rappor-metric name="Media.Controls.Cast" type="ETLD_PLUS_ONE">
+  <owner>beccahughes@chromium.org</owner>
+  <owner>media-team@chromium.org</owner>
+  <summary>
+    The URL of the page when the user casts media using the normal button.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="Media.Controls.CastOverlay" type="ETLD_PLUS_ONE">
+  <owner>beccahughes@chromium.org</owner>
+  <owner>media-team@chromium.org</owner>
+  <summary>
+    The URL of the page when the user casts media using the overlay button.
+  </summary>
+</rappor-metric>
+
 <rappor-metric name="Media.OriginUrl.EME" type="ETLD_PLUS_ONE">
   <owner>xhwang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 420ed77e..8bc9e03 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -285,6 +285,8 @@
     Power events (on/off battery power) have a similar hysteresis, but unlike
     the aforementioned properties, will not stop metric collection.
 
+    Native controls events have a similar behavior than power events.
+
     Each seek event will result in a new watch time metric being started and the
     old metric finalized as accurately as possible.
   </summary>
@@ -293,6 +295,8 @@
   <metric name="Audio.Battery"/>
   <metric name="Audio.EME"/>
   <metric name="Audio.MSE"/>
+  <metric name="Audio.NativeControlsOff"/>
+  <metric name="Audio.NativeControlsOn"/>
   <metric name="Audio.SRC"/>
   <metric name="AudioVideo.AC"/>
   <metric name="AudioVideo.All"/>
@@ -301,6 +305,8 @@
   <metric name="AudioVideo.Background.Battery"/>
   <metric name="AudioVideo.Background.EME"/>
   <metric name="AudioVideo.Background.MSE"/>
+  <metric name="AudioVideo.Background.NativeControlsOff"/>
+  <metric name="AudioVideo.Background.NativeControlsOn"/>
   <metric name="AudioVideo.Background.SRC"/>
   <metric name="AudioVideo.Battery"/>
   <metric name="AudioVideo.EME"/>
diff --git a/tools/perf/conditionally_execute b/tools/perf/conditionally_execute
new file mode 100755
index 0000000..302ab4d
--- /dev/null
+++ b/tools/perf/conditionally_execute
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main(args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--gyp-condition', '-c', type=str, required=True,
+      help=('The gyp condition that acts as the switch. If the '
+            'condition is found in environment variable GYP_DEFINES, '
+            'this will execute the target script'))
+  options, script_args = parser.parse_known_args()
+
+  # Make sure that we always execute target script with python.
+  if 'python' not in script_args[0]:
+    script_args.insert(0, sys.executable)
+
+  # A typical GYP_DEFINES string looks s.t like "foo=a bar=1 baz=c". We
+  # tokenize the string before doing string matching.
+  gyp_defines = os.environ.get('GYP_DEFINES', '').split()
+  if options.gyp_condition in gyp_defines:
+    print 'Found matching condition in GYP_DEFINES. Execute: %s' % (
+        ' '.join(script_args))
+    subprocess.check_call(script_args)
+  else:
+    print ('Not found "%s" condition in GYP_DEFINES="%s". '
+           'Skip script execution.' %
+           (options.gyp_condition, gyp_defines))
+  return 0
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/tools/perf/page_sets/key_mobile_sites.py b/tools/perf/page_sets/key_mobile_sites.py
index b73a474..df78a8f 100644
--- a/tools/perf/page_sets/key_mobile_sites.py
+++ b/tools/perf/page_sets/key_mobile_sites.py
@@ -12,8 +12,6 @@
 
   def __init__(self, url, page_set, name='', tags=None,
                action_on_load_complete=False):
-    if name == '':
-      name = url
     super(KeyMobileSitesPage, self).__init__(
         url=url, page_set=page_set, name=name,
         shared_page_state_class=shared_page_state.SharedMobilePageState,
@@ -29,8 +27,7 @@
   def __init__(self):
     super(KeyMobileSitesPageSet, self).__init__(
       archive_data_file='data/key_mobile_sites.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET,
-      verify_names=True)
+      cloud_storage_bucket=story.PARTNER_BUCKET)
 
 
     # Add pages with predefined classes that contain custom navigation logic.
diff --git a/tools/perf/page_sets/key_mobile_sites_pages.py b/tools/perf/page_sets/key_mobile_sites_pages.py
index 7754298..cf79415 100644
--- a/tools/perf/page_sets/key_mobile_sites_pages.py
+++ b/tools/perf/page_sets/key_mobile_sites_pages.py
@@ -8,8 +8,6 @@
 class KeyMobileSitesPage(page_module.Page):
 
   def __init__(self, url, page_set, name='', tags=None):
-    if name == '':
-      name = url
     super(KeyMobileSitesPage, self).__init__(
         url=url, page_set=page_set, name=name,
         shared_page_state_class=shared_page_state.SharedMobilePageState,
diff --git a/tools/perf/page_sets/key_silk_cases.py b/tools/perf/page_sets/key_silk_cases.py
index cd98a27b..ee9395f 100644
--- a/tools/perf/page_sets/key_silk_cases.py
+++ b/tools/perf/page_sets/key_silk_cases.py
@@ -17,8 +17,7 @@
     """
     super(KeySilkCasesPage, self).__init__(
         url=url, page_set=page_set, credentials_path = 'data/credentials.json',
-        shared_page_state_class=shared_page_state.SharedMobilePageState,
-        name=url)
+        shared_page_state_class=shared_page_state.SharedMobilePageState)
     self.archive_data_file = 'data/key_silk_cases.json'
     self._run_no_page_interactions = run_no_page_interactions
 
@@ -724,8 +723,7 @@
   def __init__(self, run_no_page_interactions=False):
     super(KeySilkCasesPageSet, self).__init__(
       archive_data_file='data/key_silk_cases.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET,
-      verify_names=True)
+      cloud_storage_bucket=story.PARTNER_BUCKET)
 
     self.AddStory(Page1(self, run_no_page_interactions))
     self.AddStory(Page2(self, run_no_page_interactions))
diff --git a/tools/perf/page_sets/partial_invalidation_cases.py b/tools/perf/page_sets/partial_invalidation_cases.py
index 700eed1..3db80f13 100644
--- a/tools/perf/page_sets/partial_invalidation_cases.py
+++ b/tools/perf/page_sets/partial_invalidation_cases.py
@@ -14,7 +14,7 @@
 
   def __init__(self, url, page_set):
     super(PartialInvalidationCasesPage, self).__init__(
-        url=url, page_set=page_set, name=url)
+        url=url, page_set=page_set)
 
 
 class PartialInvalidationCasesPageSet(story.StorySet):
@@ -24,7 +24,7 @@
 
   def __init__(self):
     super(PartialInvalidationCasesPageSet, self).__init__(
-        cloud_storage_bucket=story.PARTNER_BUCKET, verify_names=True)
+        cloud_storage_bucket=story.PARTNER_BUCKET)
 
     other_urls = [
         # Why: Reduced test case similar to the single page html5 spec wherein
diff --git a/tools/perf/page_sets/polymer.py b/tools/perf/page_sets/polymer.py
index bb8b998..47665aa 100644
--- a/tools/perf/page_sets/polymer.py
+++ b/tools/perf/page_sets/polymer.py
@@ -19,8 +19,7 @@
     super(PolymerPage, self).__init__(
       url=url,
       shared_page_state_class=shared_page_state.SharedMobilePageState,
-      page_set=page_set,
-      name=url)
+      page_set=page_set)
     self.script_to_evaluate_on_commit = '''
       document.addEventListener("polymer-ready", function() {
         window.__polymer_ready = true;
@@ -224,8 +223,7 @@
   def __init__(self, run_no_page_interactions=False):
     super(PolymerPageSet, self).__init__(
       archive_data_file='data/polymer.json',
-      cloud_storage_bucket=story.PUBLIC_BUCKET,
-      verify_names=True)
+      cloud_storage_bucket=story.PUBLIC_BUCKET)
 
     self.AddStory(PolymerCalculatorPage(self, run_no_page_interactions))
     self.AddStory(PolymerShadowPage(self, run_no_page_interactions))
diff --git a/tools/perf/page_sets/top_25_pages.py b/tools/perf/page_sets/top_25_pages.py
index 670aaa39..fbbc609 100644
--- a/tools/perf/page_sets/top_25_pages.py
+++ b/tools/perf/page_sets/top_25_pages.py
@@ -15,8 +15,7 @@
   def __init__(self):
     super(Top25PageSet, self).__init__(
         archive_data_file='data/top_25.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET,
-        verify_names=True)
+        cloud_storage_bucket=story.PARTNER_BUCKET)
 
     shared_desktop_state = shared_page_state.SharedDesktopPageState
     self.AddStory(top_pages.GoogleWebSearchPage(self, shared_desktop_state))
@@ -61,5 +60,4 @@
 
     for url in other_urls:
       self.AddStory(
-          page.Page(url, self, shared_page_state_class=shared_desktop_state,
-                    name=url))
+          page.Page(url, self, shared_page_state_class=shared_desktop_state))
diff --git a/tools/perf/page_sets/top_pages.py b/tools/perf/page_sets/top_pages.py
index d27d519..7f0d187 100644
--- a/tools/perf/page_sets/top_pages.py
+++ b/tools/perf/page_sets/top_pages.py
@@ -9,8 +9,6 @@
 
   def __init__(self, url, page_set, shared_page_state_class,
                name='', credentials=None):
-    if name == '':
-      name = url
     super(TopPages, self).__init__(
         url=url, page_set=page_set, name=name,
         credentials_path='data/credentials.json',
diff --git a/ui/gfx/android/view_configuration.cc b/ui/gfx/android/view_configuration.cc
index b9a0894..a79f0f9 100644
--- a/ui/gfx/android/view_configuration.cc
+++ b/ui/gfx/android/view_configuration.cc
@@ -7,7 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/synchronization/lock.h"
 #include "jni/ViewConfigurationHelper_jni.h"
 
 using base::android::AttachCurrentThread;
diff --git a/ui/gfx/codec/jpeg_codec.cc b/ui/gfx/codec/jpeg_codec.cc
index 6d926378..3dc5ad62 100644
--- a/ui/gfx/codec/jpeg_codec.cc
+++ b/ui/gfx/codec/jpeg_codec.cc
@@ -43,17 +43,6 @@
 
 }  // namespace
 
-// This method helps identify at run time which library chromium is using.
-JPEGCodec::LibraryVariant JPEGCodec::JpegLibraryVariant() {
-#if defined(USE_SYSTEM_LIBJPEG)
-  return SYSTEM_LIBJPEG;
-#elif defined(USE_LIBJPEG_TURBO)
-  return LIBJPEG_TURBO;
-#else
-  return IJG_LIBJPEG;
-#endif
-}
-
 // Encoder ---------------------------------------------------------------------
 //
 // This code is based on nsJPEGEncoder from Mozilla.
@@ -144,33 +133,6 @@
   state->out->resize(state->image_buffer_used);
 }
 
-#if !defined(JCS_EXTENSIONS)
-// Converts RGBA to RGB (removing the alpha values) to prepare to send data to
-// libjpeg. This converts one row of data in rgba with the given width in
-// pixels the the given rgb destination buffer (which should have enough space
-// reserved for the final data).
-void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++)
-    memcpy(&rgb[x * 3], &rgba[x * 4], 3);
-}
-
-// Converts BGRA to RGB by reordering the color components and dropping the
-// alpha. This converts  one row of data in rgba with the given width in
-// pixels the the given rgb destination buffer (which should have enough space
-// reserved for the final data).
-void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgra[x * 4];
-    unsigned char* pixel_out = &rgb[x * 3];
-    pixel_out[0] = pixel_in[2];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[0];
-  }
-}
-#endif  // !defined(JCS_EXTENSIONS)
-
 // This class destroys the given jpeg_compress object when it goes out of
 // scope. It simplifies the error handling in Encode (and even applies to the
 // success case).
@@ -204,9 +166,6 @@
   CompressDestroyer destroyer;
   destroyer.SetManagedObject(&cinfo);
   output->clear();
-#if !defined(JCS_EXTENSIONS)
-  unsigned char* row_buffer = NULL;
-#endif
 
   // We set up the normal JPEG error routines, then override error_exit.
   // This must be done before the call to create_compress.
@@ -222,9 +181,6 @@
     // goto using a call to longjmp."  So we delete the CompressDestroyer's
     // object manually instead.
     destroyer.DestroyManagedObject();
-#if !defined(JCS_EXTENSIONS)
-    delete[] row_buffer;
-#endif
     return false;
   }
 
@@ -233,22 +189,16 @@
 
   cinfo.image_width = w;
   cinfo.image_height = h;
-  cinfo.input_components = 3;
-#ifdef JCS_EXTENSIONS
+  cinfo.input_components = 4;
   // Choose an input colorspace and return if it is an unsupported one. Since
   // libjpeg-turbo supports all input formats used by Chromium (i.e. RGB, RGBA,
   // and BGRA), we just map the input parameters to a colorspace used by
   // libjpeg-turbo.
-  if (format == FORMAT_RGB) {
-    cinfo.input_components = 3;
-    cinfo.in_color_space = JCS_RGB;
-  } else if (format == FORMAT_RGBA ||
-             (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-    cinfo.input_components = 4;
+  if (format == FORMAT_RGBA ||
+      (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
     cinfo.in_color_space = JCS_EXT_RGBX;
   } else if (format == FORMAT_BGRA ||
              (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-    cinfo.input_components = 4;
     cinfo.in_color_space = JCS_EXT_BGRX;
   } else {
     // We can exit this function without calling jpeg_destroy_compress() because
@@ -256,9 +206,6 @@
     NOTREACHED() << "Invalid pixel format";
     return false;
   }
-#else
-  cinfo.in_color_space = JCS_RGB;
-#endif
   cinfo.data_precision = 8;
 
   jpeg_set_defaults(&cinfo);
@@ -277,7 +224,6 @@
   jpeg_start_compress(&cinfo, 1);
 
   // feed it the rows, doing necessary conversions for the color format
-#ifdef JCS_EXTENSIONS
   // This function already returns when the input format is not supported by
   // libjpeg-turbo and needs conversion. Therefore, we just encode lines without
   // conversions.
@@ -285,37 +231,6 @@
     const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
     jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
   }
-#else
-  if (format == FORMAT_RGB) {
-    // no conversion necessary
-    while (cinfo.next_scanline < cinfo.image_height) {
-      const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
-      jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
-    }
-  } else {
-    // get the correct format converter
-    void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
-    if (format == FORMAT_RGBA ||
-        (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-      converter = StripAlpha;
-    } else if (format == FORMAT_BGRA ||
-               (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-      converter = BGRAtoRGB;
-    } else {
-      NOTREACHED() << "Invalid pixel format";
-      return false;
-    }
-
-    // output row after converting
-    row_buffer = new unsigned char[w * 3];
-
-    while (cinfo.next_scanline < cinfo.image_height) {
-      converter(&input[cinfo.next_scanline * row_byte_width], w, row_buffer);
-      jpeg_write_scanlines(&cinfo, &row_buffer, 1);
-    }
-    delete[] row_buffer;
-  }
-#endif
 
   jpeg_finish_compress(&cinfo);
   return true;
@@ -398,31 +313,6 @@
 void TermSource(j_decompress_ptr cinfo) {
 }
 
-#if !defined(JCS_EXTENSIONS)
-// Converts one row of rgb data to rgba data by adding a fully-opaque alpha
-// value.
-void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
-  for (int x = 0; x < pixel_width; x++) {
-    memcpy(&rgba[x * 4], &rgb[x * 3], 3);
-    rgba[x * 4 + 3] = 0xff;
-  }
-}
-
-// Converts one row of RGB data to BGRA by reordering the color components and
-// adding alpha values of 0xff.
-void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgra[x * 3];
-    unsigned char* pixel_out = &rgb[x * 4];
-    pixel_out[0] = pixel_in[2];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[0];
-    pixel_out[3] = 0xff;
-  }
-}
-#endif  // !defined(JCS_EXTENSIONS)
-
 // This class destroys the given jpeg_decompress object when it goes out of
 // scope. It simplifies the error handling in Decode (and even applies to the
 // success case).
@@ -496,16 +386,12 @@
     case JCS_GRAYSCALE:
     case JCS_RGB:
     case JCS_YCbCr:
-#ifdef JCS_EXTENSIONS
       // Choose an output colorspace and return if it is an unsupported one.
       // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
       // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input
       // parameters to a colorspace.
-      if (format == FORMAT_RGB) {
-        cinfo.out_color_space = JCS_RGB;
-        cinfo.output_components = 3;
-      } else if (format == FORMAT_RGBA ||
-                 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
+      if (format == FORMAT_RGBA ||
+          (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
         cinfo.out_color_space = JCS_EXT_RGBX;
         cinfo.output_components = 4;
       } else if (format == FORMAT_BGRA ||
@@ -518,9 +404,6 @@
         NOTREACHED() << "Invalid pixel format";
         return false;
       }
-#else
-      cinfo.out_color_space = JCS_RGB;
-#endif
       break;
     case JCS_CMYK:
     case JCS_YCCK:
@@ -530,9 +413,6 @@
       // care about these anyway.
       return false;
   }
-#ifndef JCS_EXTENSIONS
-  cinfo.output_components = 3;
-#endif
 
   jpeg_calc_output_dimensions(&cinfo);
   *w = cinfo.output_width;
@@ -544,7 +424,6 @@
   // how to align row lengths as we do for the compressor.
   int row_read_stride = cinfo.output_width * cinfo.output_components;
 
-#ifdef JCS_EXTENSIONS
   // Create memory for a decoded image and write decoded lines to the memory
   // without conversions same as JPEGCodec::Encode().
   int row_write_stride = row_read_stride;
@@ -555,49 +434,6 @@
     if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
       return false;
   }
-#else
-  if (format == FORMAT_RGB) {
-    // easy case, row needs no conversion
-    int row_write_stride = row_read_stride;
-    output->resize(row_write_stride * cinfo.output_height);
-
-    for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
-      unsigned char* rowptr = &(*output)[row * row_write_stride];
-      if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
-        return false;
-    }
-  } else {
-    // Rows need conversion to output format: read into a temporary buffer and
-    // expand to the final one. Performance: we could avoid the extra
-    // allocation by doing the expansion in-place.
-    int row_write_stride;
-    void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
-    if (format == FORMAT_RGBA ||
-        (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-      row_write_stride = cinfo.output_width * 4;
-      converter = AddAlpha;
-    } else if (format == FORMAT_BGRA ||
-               (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-      row_write_stride = cinfo.output_width * 4;
-      converter = RGBtoBGRA;
-    } else {
-      NOTREACHED() << "Invalid pixel format";
-      jpeg_destroy_decompress(&cinfo);
-      return false;
-    }
-
-    output->resize(row_write_stride * cinfo.output_height);
-
-    std::unique_ptr<unsigned char[]> row_data(
-        new unsigned char[row_read_stride]);
-    unsigned char* rowptr = row_data.get();
-    for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
-      if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
-        return false;
-      converter(rowptr, *w, &(*output)[row * row_write_stride]);
-    }
-  }
-#endif
 
   jpeg_finish_decompress(&cinfo);
   jpeg_destroy_decompress(&cinfo);
diff --git a/ui/gfx/codec/jpeg_codec.h b/ui/gfx/codec/jpeg_codec.h
index 5d10be5..fbc07903 100644
--- a/ui/gfx/codec/jpeg_codec.h
+++ b/ui/gfx/codec/jpeg_codec.h
@@ -23,10 +23,6 @@
 class CODEC_EXPORT JPEGCodec {
  public:
   enum ColorFormat {
-    // 3 bytes per pixel (packed), in RGB order regardless of endianness.
-    // This is the native JPEG format.
-    FORMAT_RGB,
-
     // 4 bytes per pixel, in RGBA order in mem regardless of endianness.
     FORMAT_RGBA,
 
@@ -39,15 +35,6 @@
     FORMAT_SkBitmap
   };
 
-  enum LibraryVariant {
-    SYSTEM_LIBJPEG = 0,
-    LIBJPEG_TURBO,
-    IJG_LIBJPEG,
-  };
-
-  // This method helps identify at run time which library chromium is using.
-  static LibraryVariant JpegLibraryVariant();
-
   // Encodes the given raw 'input' data, with each pixel being represented as
   // given in 'format'. The encoded JPEG data will be written into the supplied
   // vector and true will be returned on success. On failure (false), the
diff --git a/ui/gfx/codec/jpeg_codec_unittest.cc b/ui/gfx/codec/jpeg_codec_unittest.cc
index 8849102e..9c4c8f1 100644
--- a/ui/gfx/codec/jpeg_codec_unittest.cc
+++ b/ui/gfx/codec/jpeg_codec_unittest.cc
@@ -88,62 +88,26 @@
   return acc / static_cast<double>(a.size());
 }
 
-static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
-  dat->resize(w * h * 3);
+static void MakeRGBAImage(int w, int h, std::vector<unsigned char>* dat) {
+  dat->resize(w * h * 4);
   for (int y = 0; y < h; y++) {
     for (int x = 0; x < w; x++) {
-      unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+      unsigned char* org_px = &(*dat)[(y * w + x) * 4];
       org_px[0] = x * 3;      // r
       org_px[1] = x * 3 + 1;  // g
       org_px[2] = x * 3 + 2;  // b
+      org_px[3] = 0xFF;       // a
     }
   }
 }
 
-TEST(JPEGCodec, EncodeDecodeRGB) {
-  int w = 20, h = 20;
-
-  // create an image with known values
-  std::vector<unsigned char> original;
-  MakeRGBImage(w, h, &original);
-
-  // encode, making sure it was compressed some
-  std::vector<unsigned char> encoded;
-  EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
-                                w * 3, jpeg_quality, &encoded));
-  EXPECT_GT(original.size(), encoded.size());
-
-  // decode, it should have the same size as the original
-  std::vector<unsigned char> decoded;
-  int outw, outh;
-  EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(),
-                                JPEGCodec::FORMAT_RGB, &decoded,
-                                &outw, &outh));
-  ASSERT_EQ(w, outw);
-  ASSERT_EQ(h, outh);
-  ASSERT_EQ(original.size(), decoded.size());
-
-  // Images must be approximately equal (compression will have introduced some
-  // minor artifacts).
-  ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded));
-}
-
 TEST(JPEGCodec, EncodeDecodeRGBA) {
   int w = 20, h = 20;
 
   // create an image with known values, a must be opaque because it will be
   // lost during compression
   std::vector<unsigned char> original;
-  original.resize(w * h * 4);
-  for (int y = 0; y < h; y++) {
-    for (int x = 0; x < w; x++) {
-      unsigned char* org_px = &original[(y * w + x) * 4];
-      org_px[0] = x * 3;      // r
-      org_px[1] = x * 3 + 1;  // g
-      org_px[2] = x * 3 + 2;  // b
-      org_px[3] = 0xFF;       // a (opaque)
-    }
-  }
+  MakeRGBAImage(w, h, &original);
 
   // encode, making sure it was compressed some
   std::vector<unsigned char> encoded;
@@ -172,31 +136,31 @@
 
   // some random data (an uncompressed image)
   std::vector<unsigned char> original;
-  MakeRGBImage(w, h, &original);
+  MakeRGBAImage(w, h, &original);
 
   // it should fail when given non-JPEG compressed data
   std::vector<unsigned char> output;
   int outw, outh;
   ASSERT_FALSE(JPEGCodec::Decode(&original[0], original.size(),
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 
   // make some compressed data
   std::vector<unsigned char> compressed;
-  ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
+  ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGBA, w, h,
                                 w * 3, jpeg_quality, &compressed));
 
   // try decompressing a truncated version
   ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size() / 2,
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 
   // corrupt it and try decompressing that
   for (int i = 10; i < 30; i++)
     compressed[i] = i;
   ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size(),
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 }
 
 // Test that we can decode JPEG images without invalid-read errors on valgrind.
@@ -207,11 +171,6 @@
   int outw, outh;
   JPEGCodec::Decode(kTopSitesMigrationTestImage,
                     arraysize(kTopSitesMigrationTestImage),
-                    JPEGCodec::FORMAT_RGB, &output,
-                    &outw, &outh);
-
-  JPEGCodec::Decode(kTopSitesMigrationTestImage,
-                    arraysize(kTopSitesMigrationTestImage),
                     JPEGCodec::FORMAT_RGBA, &output,
                     &outw, &outh);
 }
diff --git a/ui/gfx/image/image_skia_unittest.cc b/ui/gfx/image/image_skia_unittest.cc
index 2f4ef4f..df9b42d 100644
--- a/ui/gfx/image/image_skia_unittest.cc
+++ b/ui/gfx/image/image_skia_unittest.cc
@@ -17,14 +17,6 @@
 #include "ui/gfx/image/image_skia_source.h"
 #include "ui/gfx/switches.h"
 
-// Duplicated from base/threading/non_thread_safe.h so that we can be
-// good citizens there and undef the macro.
-#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
-#define ENABLE_NON_THREAD_SAFE 1
-#else
-#define ENABLE_NON_THREAD_SAFE 0
-#endif
-
 namespace gfx {
 
 namespace {
@@ -355,7 +347,7 @@
   EXPECT_FALSE(copy.BackedBySameObjectAs(unrelated));
 }
 
-#if ENABLE_NON_THREAD_SAFE
+#if DCHECK_IS_ON()
 TEST_F(ImageSkiaTest, EmptyOnThreadTest) {
   ImageSkia empty;
   test::TestOnThread empty_on_thread(&empty);
@@ -481,10 +473,7 @@
   EXPECT_TRUE(image.CanRead());
   EXPECT_FALSE(image.CanModify());
 }
-#endif  // ENABLE_NON_THREAD_SAFE
-
-// Just in case we ever get lumped together with other compilation units.
-#undef ENABLE_NON_THREAD_SAFE
+#endif  // DCHECK_IS_ON()
 
 TEST_F(ImageSkiaTest, Unscaled) {
   SkBitmap bitmap;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 5a59c2e..80cdae1 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -278,8 +278,6 @@
     "focus/focus_manager_factory.h",
     "focus/focus_search.cc",
     "focus/focus_search.h",
-    "focus/view_storage.cc",
-    "focus/view_storage.h",
     "focus/widget_focus_manager.cc",
     "focus/widget_focus_manager.h",
     "layout/box_layout.cc",
@@ -899,6 +897,7 @@
     "view_model_unittest.cc",
     "view_model_utils_unittest.cc",
     "view_targeter_unittest.cc",
+    "view_tracker_unittest.cc",
     "view_unittest.cc",
     "view_unittest_mac.mm",
     "widget/native_widget_mac_accessibility_unittest.mm",
diff --git a/ui/views/accessible_pane_view.cc b/ui/views/accessible_pane_view.cc
index a87aa9c..8d5e356 100644
--- a/ui/views/accessible_pane_view.cc
+++ b/ui/views/accessible_pane_view.cc
@@ -4,11 +4,11 @@
 
 #include "ui/views/accessible_pane_view.h"
 
-#include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/views/focus/focus_search.h"
-#include "ui/views/focus/view_storage.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/widget/widget.h"
 
 namespace views {
@@ -50,9 +50,9 @@
       escape_key_(ui::VKEY_ESCAPE, ui::EF_NONE),
       left_key_(ui::VKEY_LEFT, ui::EF_NONE),
       right_key_(ui::VKEY_RIGHT, ui::EF_NONE),
+      last_focused_view_tracker_(base::MakeUnique<ViewTracker>()),
       method_factory_(this) {
   focus_search_.reset(new AccessiblePaneViewFocusSearch(this));
-  last_focused_view_storage_id_ = ViewStorage::GetInstance()->CreateStorageID();
 }
 
 AccessiblePaneView::~AccessiblePaneView() {
@@ -69,11 +69,8 @@
     focus_manager_ = GetFocusManager();
 
   View* focused_view = focus_manager_->GetFocusedView();
-  if (focused_view && !ContainsForFocusSearch(this, focused_view)) {
-    ViewStorage* view_storage = ViewStorage::GetInstance();
-    view_storage->RemoveView(last_focused_view_storage_id_);
-    view_storage->StoreView(last_focused_view_storage_id_, focused_view);
-  }
+  if (focused_view && !ContainsForFocusSearch(this, focused_view))
+    last_focused_view_tracker_->SetView(focused_view);
 
   // Use the provided initial focus if it's visible and enabled, otherwise
   // use the first focusable child.
@@ -171,8 +168,7 @@
   switch (accelerator.key_code()) {
     case ui::VKEY_ESCAPE: {
       RemovePaneFocus();
-      View* last_focused_view = ViewStorage::GetInstance()->RetrieveView(
-          last_focused_view_storage_id_);
+      View* last_focused_view = last_focused_view_tracker_->view();
       if (last_focused_view) {
         focus_manager_->SetFocusedViewWithReason(
             last_focused_view, FocusManager::kReasonFocusRestore);
diff --git a/ui/views/accessible_pane_view.h b/ui/views/accessible_pane_view.h
index 55be727..6075569 100644
--- a/ui/views/accessible_pane_view.h
+++ b/ui/views/accessible_pane_view.h
@@ -16,6 +16,7 @@
 
 namespace views {
 class FocusSearch;
+class ViewTracker;
 
 // This class provides keyboard access to any view that extends it, typically
 // a toolbar.  The user sets focus to a control in this view by pressing
@@ -116,8 +117,8 @@
   ui::Accelerator left_key_;
   ui::Accelerator right_key_;
 
-  // View storage id for the last focused view that's not within this pane.
-  int last_focused_view_storage_id_;
+  // Holds the last focused view that's not within this pane.
+  std::unique_ptr<ViewTracker> last_focused_view_tracker_;
 
   friend class AccessiblePaneViewFocusSearch;
 
diff --git a/ui/views/bubble/bubble_dialog_delegate.cc b/ui/views/bubble/bubble_dialog_delegate.cc
index 5688543b..b8be537 100644
--- a/ui/views/bubble/bubble_dialog_delegate.cc
+++ b/ui/views/bubble/bubble_dialog_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/bubble/bubble_dialog_delegate.h"
 
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -14,9 +15,9 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 #include "ui/views/window/dialog_client_view.h"
@@ -158,7 +159,7 @@
 }
 
 View* BubbleDialogDelegateView::GetAnchorView() const {
-  return ViewStorage::GetInstance()->RetrieveView(anchor_view_storage_id_);
+  return anchor_view_tracker_->view();
 }
 
 gfx::Rect BubbleDialogDelegateView::GetAnchorRect() const {
@@ -206,15 +207,15 @@
 BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view,
                                                    BubbleBorder::Arrow arrow)
     : close_on_deactivate_(true),
-      anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()),
-      anchor_widget_(NULL),
+      anchor_view_tracker_(base::MakeUnique<ViewTracker>()),
+      anchor_widget_(nullptr),
       arrow_(arrow),
       mirror_arrow_in_rtl_(PlatformStyle::kMirrorBubbleArrowInRTLByDefault),
       shadow_(BubbleBorder::SMALL_SHADOW),
       color_explicitly_set_(false),
       accept_events_(true),
       adjust_if_offscreen_(true),
-      parent_window_(NULL) {
+      parent_window_(nullptr) {
   LayoutProvider* provider = LayoutProvider::Get();
   margins_ = provider->GetInsetsMetric(INSETS_BUBBLE_CONTENTS);
   title_margins_ = provider->GetInsetsMetric(INSETS_BUBBLE_TITLE);
@@ -262,12 +263,7 @@
     }
   }
 
-  // Remove the old storage item and set the new (if there is one).
-  ViewStorage* view_storage = ViewStorage::GetInstance();
-  if (view_storage->RetrieveView(anchor_view_storage_id_))
-    view_storage->RemoveView(anchor_view_storage_id_);
-  if (anchor_view)
-    view_storage->StoreView(anchor_view_storage_id_, anchor_view);
+  anchor_view_tracker_->SetView(anchor_view);
 
   // Do not update anchoring for NULL views; this could indicate that our
   // NativeWindow is being destroyed, so it would be dangerous for us to update
diff --git a/ui/views/bubble/bubble_dialog_delegate.h b/ui/views/bubble/bubble_dialog_delegate.h
index 81a3a4c..e027988d 100644
--- a/ui/views/bubble/bubble_dialog_delegate.h
+++ b/ui/views/bubble/bubble_dialog_delegate.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_H_
 #define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_H_
 
+#include <memory>
+
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -20,6 +22,7 @@
 namespace views {
 
 class BubbleFrameView;
+class ViewTracker;
 
 // BubbleDialogDelegateView is a special DialogDelegateView for bubbles.
 class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
@@ -166,9 +169,9 @@
   bool close_on_deactivate_;
 
   // The view and widget to which this bubble is anchored. Since an anchor view
-  // can be deleted without notice, we store it in the ViewStorage and retrieve
+  // can be deleted without notice, we store it in a ViewTracker and retrieve
   // it from there. It will make sure that the view is still valid.
-  const int anchor_view_storage_id_;
+  std::unique_ptr<ViewTracker> anchor_view_tracker_;
   Widget* anchor_widget_;
 
   // The anchor rect used in the absence of an anchor view.
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 7c44dd17..f66a3e4 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -30,10 +30,10 @@
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/drag_utils.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/mouse_constants.h"
 #include "ui/views/view.h"
 #include "ui/views/view_constants.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/tooltip_manager.h"
@@ -684,7 +684,7 @@
   if (!part.is_scroll() && part.menu &&
       !(part.menu->HasSubmenu() &&
         (event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
-    if (GetActiveMouseView()) {
+    if (active_mouse_view_tracker_->view()) {
       SendMouseReleaseToActiveView(source, event);
       return;
     }
@@ -1186,7 +1186,7 @@
   if (!blocking_run_)
     return;
 
-  DCHECK(!GetActiveMouseView());
+  DCHECK(!active_mouse_view_tracker_->view());
 
   MenuPart part = GetMenuPart(source, event->location());
   if (part.is_scroll())
@@ -1390,7 +1390,7 @@
       valid_drop_coordinates_(false),
       last_drop_operation_(MenuDelegate::DROP_UNKNOWN),
       showing_submenu_(false),
-      active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()),
+      active_mouse_view_tracker_(base::MakeUnique<ViewTracker>()),
       hot_button_(nullptr),
       delegate_(delegate),
       is_combobox_(false),
@@ -1837,7 +1837,7 @@
 void MenuController::MenuChildrenChanged(MenuItemView* item) {
   DCHECK(item);
   // Menu shouldn't be updated during drag operation.
-  DCHECK(!GetActiveMouseView());
+  DCHECK(!active_mouse_view_tracker_->view());
 
   // If the current item or pending item is a descendant of the item
   // that changed, move the selection back to the changed item.
@@ -2479,11 +2479,11 @@
     if (target == target_menu || !target->enabled())
       target = NULL;
   }
-  View* active_mouse_view = GetActiveMouseView();
+  View* active_mouse_view = active_mouse_view_tracker_->view();
   if (target != active_mouse_view) {
     SendMouseCaptureLostToActiveView();
     active_mouse_view = target;
-    SetActiveMouseView(active_mouse_view);
+    active_mouse_view_tracker_->SetView(active_mouse_view);
     if (active_mouse_view) {
       gfx::Point target_point(target_menu_loc);
       View::ConvertPointToTarget(
@@ -2512,7 +2512,7 @@
 
 void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source,
                                                   const ui::MouseEvent& event) {
-  View* active_mouse_view = GetActiveMouseView();
+  View* active_mouse_view = active_mouse_view_tracker_->view();
   if (!active_mouse_view)
     return;
 
@@ -2523,34 +2523,23 @@
   ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc,
                                ui::EventTimeForNow(), event.flags(),
                                event.changed_button_flags());
-  // Reset active mouse view before sending mouse released. That way if it calls
-  // back to us, we aren't in a weird state.
-  SetActiveMouseView(NULL);
+  // Reset the active mouse view before sending mouse released. That way if it
+  // calls back to us, we aren't in a weird state.
+  active_mouse_view_tracker_->Clear();
   active_mouse_view->OnMouseReleased(release_event);
 }
 
 void MenuController::SendMouseCaptureLostToActiveView() {
-  View* active_mouse_view = GetActiveMouseView();
+  View* active_mouse_view = active_mouse_view_tracker_->view();
   if (!active_mouse_view)
     return;
 
-  // Reset the active_mouse_view_ before sending mouse capture lost. That way if
+  // Reset the active mouse view before sending mouse capture lost. That way if
   // it calls back to us, we aren't in a weird state.
-  SetActiveMouseView(NULL);
+  active_mouse_view_tracker_->Clear();
   active_mouse_view->OnMouseCaptureLost();
 }
 
-void MenuController::SetActiveMouseView(View* view) {
-  if (view)
-    ViewStorage::GetInstance()->StoreView(active_mouse_view_id_, view);
-  else
-    ViewStorage::GetInstance()->RemoveView(active_mouse_view_id_);
-}
-
-View* MenuController::GetActiveMouseView() {
-  return ViewStorage::GetInstance()->RetrieveView(active_mouse_view_id_);
-}
-
 void MenuController::SetExitType(ExitType type) {
   exit_type_ = type;
 }
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 16ac2d4b..06fc19b 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -36,6 +36,7 @@
 class MouseEvent;
 class SubmenuView;
 class View;
+class ViewTracker;
 
 #if defined(USE_AURA)
 class MenuPreTargetHandler;
@@ -524,10 +525,6 @@
   // it to null.
   void SendMouseCaptureLostToActiveView();
 
-  // Sets/gets the active mouse view. See UpdateActiveMouseView() for details.
-  void SetActiveMouseView(View* view);
-  View* GetActiveMouseView();
-
   // Sets exit type. Calling this can terminate the active nested message-loop.
   void SetExitType(ExitType type);
 
@@ -644,9 +641,9 @@
   // The lock to keep the menu button pressed while a menu is visible.
   std::unique_ptr<MenuButton::PressedLock> pressed_lock_;
 
-  // ViewStorage id used to store the view mouse drag events are forwarded to.
-  // See UpdateActiveMouseView() for details.
-  const int active_mouse_view_id_;
+  // ViewTracker used to store the View mouse drag events are forwarded to. See
+  // UpdateActiveMouseView() for details.
+  std::unique_ptr<ViewTracker> active_mouse_view_tracker_;
 
   // Current hot tracked child button if any.
   CustomButton* hot_button_;
diff --git a/ui/views/focus/external_focus_tracker.cc b/ui/views/focus/external_focus_tracker.cc
index 293c2e0..df1ab7c 100644
--- a/ui/views/focus/external_focus_tracker.cc
+++ b/ui/views/focus/external_focus_tracker.cc
@@ -5,25 +5,24 @@
 #include "ui/views/focus/external_focus_tracker.h"
 
 #include "base/logging.h"
-#include "ui/views/focus/view_storage.h"
+#include "base/memory/ptr_util.h"
 #include "ui/views/view.h"
+#include "ui/views/view_tracker.h"
 
 namespace views {
 
 ExternalFocusTracker::ExternalFocusTracker(View* parent_view,
                                            FocusManager* focus_manager)
     : focus_manager_(focus_manager),
-      parent_view_(parent_view) {
+      parent_view_(parent_view),
+      last_focused_view_tracker_(base::MakeUnique<ViewTracker>()) {
   DCHECK(parent_view);
-  view_storage_ = ViewStorage::GetInstance();
-  last_focused_view_storage_id_ = view_storage_->CreateStorageID();
   // Store the view which is focused when we're created.
   if (focus_manager_)
     StartTracking();
 }
 
 ExternalFocusTracker::~ExternalFocusTracker() {
-  view_storage_->RemoveView(last_focused_view_storage_id_);
   if (focus_manager_)
     focus_manager_->RemoveFocusChangeListener(this);
 }
@@ -42,8 +41,7 @@
 }
 
 void ExternalFocusTracker::FocusLastFocusedExternalView() {
-  View* last_focused_view =
-      view_storage_->RetrieveView(last_focused_view_storage_id_);
+  View* last_focused_view = last_focused_view_tracker_->view();
   if (last_focused_view)
     last_focused_view->RequestFocus();
 }
@@ -57,11 +55,7 @@
 }
 
 void ExternalFocusTracker::StoreLastFocusedView(View* view) {
-  view_storage_->RemoveView(last_focused_view_storage_id_);
-  // If the view is NULL, remove the last focused view from storage, but don't
-  // try to store NULL.
-  if (view != NULL)
-    view_storage_->StoreView(last_focused_view_storage_id_, view);
+  last_focused_view_tracker_->SetView(view);
 }
 
 void ExternalFocusTracker::StartTracking() {
diff --git a/ui/views/focus/external_focus_tracker.h b/ui/views/focus/external_focus_tracker.h
index 50f5ab7..4c93a9a 100644
--- a/ui/views/focus/external_focus_tracker.h
+++ b/ui/views/focus/external_focus_tracker.h
@@ -12,7 +12,7 @@
 namespace views {
 
 class View;
-class ViewStorage;
+class ViewTracker;
 
 // ExternalFocusTracker tracks the last focused view which belongs to the
 // provided focus manager and is not either the provided parent view or one of
@@ -60,17 +60,13 @@
   // Focus manager which we are a listener for.
   FocusManager* focus_manager_;
 
-  // ID of the last focused view, which we store in view_storage_.
-  int last_focused_view_storage_id_;
-
-  // Used to store the last focused view which is not a child of
-  // ExternalFocusTracker.
-  ViewStorage* view_storage_;
-
   // The parent view of views which we should not track focus changes to. We
   // also do not track changes to parent_view_ itself.
   View* parent_view_;
 
+  // Holds the last focused view.
+  std::unique_ptr<ViewTracker> last_focused_view_tracker_;
+
   DISALLOW_COPY_AND_ASSIGN(ExternalFocusTracker);
 };
 
diff --git a/ui/views/focus/view_storage.cc b/ui/views/focus/view_storage.cc
deleted file mode 100644
index 4c519f3..0000000
--- a/ui/views/focus/view_storage.cc
+++ /dev/null
@@ -1,92 +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 "ui/views/focus/view_storage.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-
-namespace views {
-
-// static
-ViewStorage* ViewStorage::GetInstance() {
-  return base::Singleton<ViewStorage>::get();
-}
-
-ViewStorage::ViewStorage() : view_storage_next_id_(0) {
-}
-
-ViewStorage::~ViewStorage() {}
-
-int ViewStorage::CreateStorageID() {
-  return view_storage_next_id_++;
-}
-
-void ViewStorage::StoreView(int storage_id, View* view) {
-  DCHECK(view);
-
-  if (id_to_view_.find(storage_id) != id_to_view_.end()) {
-    NOTREACHED();
-    RemoveView(storage_id);
-  }
-
-  id_to_view_[storage_id] = view;
-  view_to_ids_[view].push_back(storage_id);
-}
-
-View* ViewStorage::RetrieveView(int storage_id) {
-  auto iter = id_to_view_.find(storage_id);
-  if (iter == id_to_view_.end())
-    return nullptr;
-  return iter->second;
-}
-
-void ViewStorage::RemoveView(int storage_id) {
-  EraseView(storage_id, false);
-}
-
-void ViewStorage::ViewRemoved(View* removed) {
-  // Let's first retrieve the ids for that view.
-  auto ids_iter = view_to_ids_.find(removed);
-
-  if (ids_iter == view_to_ids_.end()) {
-    // That view is not in the view storage.
-    return;
-  }
-
-  const std::vector<int>& ids = ids_iter->second;
-  DCHECK(!ids.empty());
-  EraseView(ids[0], true);
-}
-
-void ViewStorage::EraseView(int storage_id, bool remove_all_ids) {
-  // Remove the view from id_to_view_location_.
-  auto view_iter = id_to_view_.find(storage_id);
-  if (view_iter == id_to_view_.end())
-    return;
-
-  View* view = view_iter->second;
-  id_to_view_.erase(view_iter);
-
-  // Also update view_to_ids_.
-  auto ids_iter = view_to_ids_.find(view);
-  DCHECK(ids_iter != view_to_ids_.end());
-  std::vector<int>& ids = ids_iter->second;
-
-  if (remove_all_ids) {
-    for (int id : ids)
-      id_to_view_.erase(id);
-    view_to_ids_.erase(ids_iter);
-  } else if (ids.size() == 1) {
-    view_to_ids_.erase(ids_iter);
-  } else {
-    auto id_iter = std::find(ids.begin(), ids.end(), storage_id);
-    DCHECK(id_iter != ids.end());
-    ids.erase(id_iter);
-  }
-}
-
-}  // namespace views
diff --git a/ui/views/focus/view_storage.h b/ui/views/focus/view_storage.h
deleted file mode 100644
index 73235c1..0000000
--- a/ui/views/focus/view_storage.h
+++ /dev/null
@@ -1,79 +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 UI_VIEWS_FOCUS_VIEW_STORAGE_H_
-#define UI_VIEWS_FOCUS_VIEW_STORAGE_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <vector>
-
-#include "base/macros.h"
-#include "ui/views/views_export.h"
-
-namespace base {
-template <typename T> struct DefaultSingletonTraits;
-}
-
-// This class is a simple storage place for storing/retrieving views.  It is
-// used for example in the FocusManager to store/restore focused views when the
-// main window becomes active/inactive.
-// It automatically removes a view from the storage if the view is removed from
-// the tree hierarchy or when the view is destroyed, which ever comes first.
-//
-// To use it, you first need to create a view storage id that can then be used
-// to store/retrieve views.
-
-namespace views {
-class View;
-
-class VIEWS_EXPORT ViewStorage {
- public:
-  // Returns the global ViewStorage instance.
-  // It is guaranted to be non NULL.
-  static ViewStorage* GetInstance();
-
-  // Returns a unique storage id that can be used to store/retrieve views.
-  int CreateStorageID();
-
-  // Associates |view| with the specified |storage_id|.
-  void StoreView(int storage_id, View* view);
-
-  // Returns the view associated with |storage_id| if any, NULL otherwise.
-  View* RetrieveView(int storage_id);
-
-  // Removes the view associated with |storage_id| if any.
-  void RemoveView(int storage_id);
-
-  // Notifies the ViewStorage that a view was removed from its parent somewhere.
-  void ViewRemoved(View* removed);
-
-  size_t view_count() const { return view_to_ids_.size(); }
-
- private:
-  friend struct base::DefaultSingletonTraits<ViewStorage>;
-
-  ViewStorage();
-  ~ViewStorage();
-
-  // Removes the view associated with |storage_id|. If |remove_all_ids| is true,
-  // all other mapping pointing to the same view are removed as well.
-  void EraseView(int storage_id, bool remove_all_ids);
-
-  // Next id for the view storage.
-  int view_storage_next_id_;
-
-  // The association id to View used for the view storage.
-  std::map<int, View*> id_to_view_;
-
-  // Association View to id, used to speed up view notification removal.
-  std::map<View*, std::vector<int>> view_to_ids_;
-
-  DISALLOW_COPY_AND_ASSIGN(ViewStorage);
-};
-
-}  // namespace views
-
-#endif  // UI_VIEWS_FOCUS_VIEW_STORAGE_H_
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 8fbd867..e62c3951 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -53,7 +53,6 @@
 #include "ui/views/border.h"
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/drag_controller.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/layout/layout_manager.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/views_delegate.h"
@@ -157,8 +156,6 @@
   if (parent_)
     parent_->RemoveChildView(this);
 
-  ViewStorage::GetInstance()->ViewRemoved(this);
-
   {
     internal::ScopedChildrenLock lock(this);
     for (auto* child : children_) {
@@ -2431,15 +2428,6 @@
       context_menu_controller_ : 0;
   View::DragInfo* drag_info = GetDragInfo();
 
-  // TODO(sky): for debugging 360238.
-  int storage_id = 0;
-  if (event.IsOnlyRightMouseButton() && context_menu_controller &&
-      kContextMenuOnMousePress && HitTestPoint(event.location())) {
-    ViewStorage* view_storage = ViewStorage::GetInstance();
-    storage_id = view_storage->CreateStorageID();
-    view_storage->StoreView(storage_id, this);
-  }
-
   const bool enabled = enabled_;
   const bool result = OnMousePressed(event);
 
@@ -2450,10 +2438,8 @@
       kContextMenuOnMousePress) {
     // Assume that if there is a context menu controller we won't be deleted
     // from mouse pressed.
-    gfx::Point location(event.location());
-    if (HitTestPoint(location)) {
-      if (storage_id != 0)
-        CHECK_EQ(this, ViewStorage::GetInstance()->RetrieveView(storage_id));
+    if (HitTestPoint(event.location())) {
+      gfx::Point location(event.location());
       ConvertPointToScreen(this, &location);
       ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
       return true;
diff --git a/ui/views/view_tracker_unittest.cc b/ui/views/view_tracker_unittest.cc
new file mode 100644
index 0000000..34f48cf0
--- /dev/null
+++ b/ui/views/view_tracker_unittest.cc
@@ -0,0 +1,24 @@
+// 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 "ui/views/view_tracker.h"
+
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+using ViewTrackerTest = ViewsTestBase;
+
+TEST_F(ViewTrackerTest, RemovedOnDelete) {
+  ViewTracker tracker;
+  {
+    View view;
+    tracker.SetView(&view);
+    EXPECT_EQ(&view, tracker.view());
+  }
+  EXPECT_EQ(nullptr, tracker.view());
+}
+
+}  // namespace views
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index e45cbb7..49efb8a 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -40,7 +40,6 @@
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/widget/native_widget.h"
@@ -1237,94 +1236,6 @@
   View::SchedulePaintInRect(rect);
 }
 
-TEST_F(ViewTest, RemoveNotification) {
-  ViewStorage* vs = ViewStorage::GetInstance();
-  Widget* widget = new Widget;
-  widget->Init(CreateParams(Widget::InitParams::TYPE_POPUP));
-  View* root_view = widget->GetRootView();
-
-  View* v1 = new View;
-  int s1 = vs->CreateStorageID();
-  vs->StoreView(s1, v1);
-  root_view->AddChildView(v1);
-  View* v11 = new View;
-  int s11 = vs->CreateStorageID();
-  vs->StoreView(s11, v11);
-  v1->AddChildView(v11);
-  View* v111 = new View;
-  int s111 = vs->CreateStorageID();
-  vs->StoreView(s111, v111);
-  v11->AddChildView(v111);
-  View* v112 = new View;
-  int s112 = vs->CreateStorageID();
-  vs->StoreView(s112, v112);
-  v11->AddChildView(v112);
-  View* v113 = new View;
-  int s113 = vs->CreateStorageID();
-  vs->StoreView(s113, v113);
-  v11->AddChildView(v113);
-  View* v1131 = new View;
-  int s1131 = vs->CreateStorageID();
-  vs->StoreView(s1131, v1131);
-  v113->AddChildView(v1131);
-  View* v12 = new View;
-  int s12 = vs->CreateStorageID();
-  vs->StoreView(s12, v12);
-  v1->AddChildView(v12);
-
-  View* v2 = new View;
-  int s2 = vs->CreateStorageID();
-  vs->StoreView(s2, v2);
-  root_view->AddChildView(v2);
-  View* v21 = new View;
-  int s21 = vs->CreateStorageID();
-  vs->StoreView(s21, v21);
-  v2->AddChildView(v21);
-  View* v211 = new View;
-  int s211 = vs->CreateStorageID();
-  vs->StoreView(s211, v211);
-  v21->AddChildView(v211);
-
-  size_t stored_views = vs->view_count();
-
-  // Try removing a leaf view.
-  v21->RemoveChildView(v211);
-  EXPECT_EQ(stored_views - 1, vs->view_count());
-  EXPECT_EQ(NULL, vs->RetrieveView(s211));
-  delete v211;  // We won't use this one anymore.
-
-  // Now try removing a view with a hierarchy of depth 1.
-  v11->RemoveChildView(v113);
-  EXPECT_EQ(stored_views - 3, vs->view_count());
-  EXPECT_EQ(NULL, vs->RetrieveView(s113));
-  EXPECT_EQ(NULL, vs->RetrieveView(s1131));
-  delete v113;  // We won't use this one anymore.
-
-  // Now remove even more.
-  root_view->RemoveChildView(v1);
-  EXPECT_EQ(NULL, vs->RetrieveView(s1));
-  EXPECT_EQ(NULL, vs->RetrieveView(s11));
-  EXPECT_EQ(NULL, vs->RetrieveView(s12));
-  EXPECT_EQ(NULL, vs->RetrieveView(s111));
-  EXPECT_EQ(NULL, vs->RetrieveView(s112));
-
-  // Put v1 back for more tests.
-  root_view->AddChildView(v1);
-  vs->StoreView(s1, v1);
-
-  // Synchronously closing the window deletes the view hierarchy, which should
-  // remove all its views from ViewStorage.
-  widget->CloseNow();
-  EXPECT_EQ(stored_views - 10, vs->view_count());
-  EXPECT_EQ(NULL, vs->RetrieveView(s1));
-  EXPECT_EQ(NULL, vs->RetrieveView(s12));
-  EXPECT_EQ(NULL, vs->RetrieveView(s11));
-  EXPECT_EQ(NULL, vs->RetrieveView(s12));
-  EXPECT_EQ(NULL, vs->RetrieveView(s21));
-  EXPECT_EQ(NULL, vs->RetrieveView(s111));
-  EXPECT_EQ(NULL, vs->RetrieveView(s112));
-}
-
 namespace {
 
 void RotateCounterclockwise(gfx::Transform* transform) {
@@ -4541,17 +4452,6 @@
   EXPECT_EQ(View::FocusBehavior::ACCESSIBLE_ONLY, view.focus_behavior());
 }
 
-// Verifies when a view is deleted it is removed from ViewStorage.
-TEST_F(ViewTest, UpdateViewStorageOnDelete) {
-  ViewStorage* view_storage = ViewStorage::GetInstance();
-  const int storage_id = view_storage->CreateStorageID();
-  {
-    View view;
-    view_storage->StoreView(storage_id, &view);
-  }
-  EXPECT_TRUE(view_storage->RetrieveView(storage_id) == NULL);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // NativeTheme
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index fbce29e..a039443 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -28,7 +28,6 @@
 #include "ui/views/event_monitor.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/focus/focus_manager_factory.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/focus/widget_focus_manager.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/native_widget_private.h"
@@ -426,7 +425,6 @@
     FocusManager* focus_manager = GetFocusManager();
     if (focus_manager)
       focus_manager->ViewRemoved(details.child);
-    ViewStorage::GetInstance()->ViewRemoved(details.child);
     native_widget_->ViewRemoved(details.child);
   }
 }