diff --git a/AUTHORS b/AUTHORS
index 888a1a3..342fe07 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -133,6 +133,7 @@
 Changyeon Kim <cyzero.kim@samsung.com>
 Chansik Yun <chansik.yun@gmail.com>
 Chaobin Zhang <zhchbin@gmail.com>
+Choongwoo Han <cwhan.tunz@gmail.com>
 Chris Greene <cwgreene@amazon.com>
 Chris Harrelson <chrishtr@gmail.com>
 Chris Nardi <hichris123@gmail.com>
diff --git a/DEPS b/DEPS
index 1e33a23..e7fc4413 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5bd984892db1b5a2c3f76d51d8d77499afd853c5',
+  'skia_revision': '0fa156fcfba3b430801b5448ddfc254732bf7386',
   # 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': 'ad5116b06454d0cf88d7f4f362833a23b84e26b7',
+  'v8_revision': 'a41f32d2aa7d677a89ba2c2b4a83d3c76895efd7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '84b596d64aab8f8c6dd6145e9c210b145fc5631a',
+  'pdfium_revision': 'e6baec592965d6e7e8969ae22910306ff4fed98b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -232,7 +232,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '19ef130fe788d5d0471fa1b301cd622ae73666b7', # commit position 17638
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '1abcfb59a01e17050dc41b5635e52ee591962018', # commit position 17657
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/ash/laser/laser_pointer_view.cc b/ash/laser/laser_pointer_view.cc
index 026f950..aa39b88 100644
--- a/ash/laser/laser_pointer_view.cc
+++ b/ash/laser/laser_pointer_view.cc
@@ -522,8 +522,8 @@
       // Set the radius and opacity based on the distance.
       float current_radius = LinearInterpolate(
           kPointInitialRadius, kPointFinalRadius, current_point.age);
-      current_opacity = int{LinearInterpolate(
-          kPointInitialOpacity, kPointFinalOpacity, current_point.age)};
+      current_opacity = static_cast<int>(LinearInterpolate(
+          kPointInitialOpacity, kPointFinalOpacity, current_point.age));
 
       // If we draw laser_points_ that are within a stroke width of each other,
       // the result will be very jagged, unless we are on the last point, then
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index 406f328..c286c2ce 100644
--- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -621,6 +621,18 @@
     }
 
     /**
+     * Get the URI for a downloaded file.
+     *
+     * @param file A downloaded file.
+     * @return URI for |file|.
+     */
+    public static Uri getUriForDownloadedFile(File file) {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
+                ? FileUtils.getUriForFile(file)
+                : Uri.fromFile(file);
+    }
+
+    /**
      * @see android.view.Window#FEATURE_INDETERMINATE_PROGRESS
      */
     public static void setWindowIndeterminateProgress(Window window) {
diff --git a/base/android/java/src/org/chromium/base/FileUtils.java b/base/android/java/src/org/chromium/base/FileUtils.java
index 2ad70dd..931b0c4 100644
--- a/base/android/java/src/org/chromium/base/FileUtils.java
+++ b/base/android/java/src/org/chromium/base/FileUtils.java
@@ -5,6 +5,7 @@
 package org.chromium.base;
 
 import android.content.Context;
+import android.net.Uri;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -86,4 +87,27 @@
         }
         return false;
     }
+
+    /**
+     * Returns a URI that points at the file.
+     * @param file File to get a URI for.
+     * @return URI that points at that file, either as a content:// URI or a file:// URI.
+     */
+    public static Uri getUriForFile(File file) {
+        // TODO(crbug/709584): Uncomment this when http://crbug.com/709584 has been fixed.
+        // assert !ThreadUtils.runningOnUiThread();
+        Uri uri = null;
+
+        try {
+            // Try to obtain a content:// URI, which is preferred to a file:// URI so that
+            // receiving apps don't attempt to determine the file's mime type (which often fails).
+            uri = ContentUriUtils.getContentUriFromFile(file);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Could not create content uri: " + e);
+        }
+
+        if (uri == null) uri = Uri.fromFile(file);
+
+        return uri;
+    }
 }
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc
index 0ab7c3d..3c67234 100644
--- a/base/debug/activity_analyzer.cc
+++ b/base/debug/activity_analyzer.cc
@@ -172,25 +172,6 @@
   return iter->second.data;
 }
 
-const ActivityUserData::Snapshot&
-GlobalActivityAnalyzer::GetGlobalDataSnapshot() {
-  global_data_snapshot_.clear();
-
-  PersistentMemoryAllocator::Reference ref =
-      PersistentMemoryAllocator::Iterator(allocator_.get())
-          .GetNextOfType(GlobalActivityTracker::kTypeIdGlobalDataRecord);
-  void* memory = allocator_->GetAsArray<char>(
-      ref, GlobalActivityTracker::kTypeIdGlobalDataRecord,
-      PersistentMemoryAllocator::kSizeAny);
-  if (memory) {
-    size_t size = allocator_->GetAllocSize(ref);
-    const ActivityUserData global_data(memory, size);
-    global_data.CreateSnapshot(&global_data_snapshot_);
-  }
-
-  return global_data_snapshot_;
-}
-
 std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
   std::vector<std::string> messages;
   PersistentMemoryAllocator::Reference ref;
diff --git a/base/debug/activity_analyzer.h b/base/debug/activity_analyzer.h
index 2200537a..e98046e 100644
--- a/base/debug/activity_analyzer.h
+++ b/base/debug/activity_analyzer.h
@@ -176,9 +176,6 @@
   // returned if the process is not known.
   const ActivityUserData::Snapshot& GetProcessDataSnapshot(int64_t pid);
 
-  // Extract the global data.
-  const ActivityUserData::Snapshot& GetGlobalDataSnapshot();
-
   // Gets all log messages stored within.
   std::vector<std::string> GetLogMessages();
 
@@ -236,9 +233,6 @@
   AnalyzerMap::iterator analyzers_iterator_;
   int64_t analyzers_iterator_pid_;
 
-  // Snapshot of the global data.
-  ActivityUserData::Snapshot global_data_snapshot_;
-
   DISALLOW_COPY_AND_ASSIGN(GlobalActivityAnalyzer);
 };
 
diff --git a/base/debug/activity_analyzer_unittest.cc b/base/debug/activity_analyzer_unittest.cc
index 82889dc..31871f5d 100644
--- a/base/debug/activity_analyzer_unittest.cc
+++ b/base/debug/activity_analyzer_unittest.cc
@@ -314,6 +314,7 @@
 }
 
 TEST_F(ActivityAnalyzerTest, GlobalUserDataTest) {
+  const int64_t pid = GetCurrentProcId();
   GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
 
   const char string1[] = "foo";
@@ -324,18 +325,21 @@
   GlobalActivityAnalyzer global_analyzer(MakeUnique<PersistentMemoryAllocator>(
       const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
 
-  ActivityUserData& global_data = GlobalActivityTracker::Get()->global_data();
-  global_data.Set("raw", "foo", 3);
-  global_data.SetString("string", "bar");
-  global_data.SetChar("char", '9');
-  global_data.SetInt("int", -9999);
-  global_data.SetUint("uint", 9999);
-  global_data.SetBool("bool", true);
-  global_data.SetReference("ref", string1, sizeof(string1));
-  global_data.SetStringReference("sref", string2);
+  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+  ASSERT_NE(0U, process_data.id());
+  process_data.Set("raw", "foo", 3);
+  process_data.SetString("string", "bar");
+  process_data.SetChar("char", '9');
+  process_data.SetInt("int", -9999);
+  process_data.SetUint("uint", 9999);
+  process_data.SetBool("bool", true);
+  process_data.SetReference("ref", string1, sizeof(string1));
+  process_data.SetStringReference("sref", string2);
 
+  int64_t first_pid = global_analyzer.GetFirstProcess();
+  DCHECK_EQ(pid, first_pid);
   const ActivityUserData::Snapshot& snapshot =
-      global_analyzer.GetGlobalDataSnapshot();
+      global_analyzer.GetProcessDataSnapshot(pid);
   ASSERT_TRUE(ContainsKey(snapshot, "raw"));
   EXPECT_EQ("foo", snapshot.at("raw").Get().as_string());
   ASSERT_TRUE(ContainsKey(snapshot, "string"));
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 5081c1c..0e8db93 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -38,7 +38,6 @@
 // pairs) globally or associated with ActivityData entries.
 const size_t kUserDataSize = 1 << 10;     // 1 KiB
 const size_t kProcessDataSize = 4 << 10;  // 4 KiB
-const size_t kGlobalDataSize = 16 << 10;  // 16 KiB
 const size_t kMaxUserDataNameLength =
     static_cast<size_t>(std::numeric_limits<uint8_t>::max());
 
@@ -1463,8 +1462,8 @@
   if (task_runner && !task_runner->RunsTasksOnCurrentThread()) {
     task_runner->PostTask(
         FROM_HERE,
-        Bind(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this), pid,
-             now_stamp, exit_code, Passed(&command_line)));
+        BindOnce(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this),
+                 pid, now_stamp, exit_code, Passed(&command_line)));
     return;
   }
 
@@ -1599,7 +1598,7 @@
 void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
                                              StringPiece group_name) {
   const std::string key = std::string("FieldTrial.") + trial_name;
-  global_data_.SetString(key, group_name);
+  process_data_.SetString(key, group_name);
 }
 
 GlobalActivityTracker::GlobalActivityTracker(
@@ -1631,14 +1630,7 @@
                         kTypeIdProcessDataRecord,
                         kProcessDataSize),
                     kProcessDataSize,
-                    process_id_),
-      global_data_(
-          allocator_->GetAsArray<char>(
-              allocator_->Allocate(kGlobalDataSize, kTypeIdGlobalDataRecord),
-              kTypeIdGlobalDataRecord,
-              kGlobalDataSize),
-          kGlobalDataSize,
-          process_id_) {
+                    process_id_) {
   DCHECK_NE(0, process_id_);
 
   // Ensure that there is no other global object and then make this one such.
@@ -1648,8 +1640,6 @@
   // The data records must be iterable in order to be found by an analyzer.
   allocator_->MakeIterable(allocator_->GetAsReference(
       process_data_.GetBaseAddress(), kTypeIdProcessDataRecord));
-  allocator_->MakeIterable(allocator_->GetAsReference(
-      global_data_.GetBaseAddress(), kTypeIdGlobalDataRecord));
 
   // Note that this process has launched.
   SetProcessPhase(PROCESS_LAUNCHED);
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index c8cf1e9..c968f38 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -764,7 +764,6 @@
     kTypeIdUserDataRecord = 0x615EDDD7 + 3,    // SHA1(UserDataRecord) v3
     kTypeIdGlobalLogMessage = 0x4CF434F9 + 1,  // SHA1(GlobalLogMessage) v1
     kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,
-    kTypeIdGlobalDataRecord = kTypeIdUserDataRecord + 0x200,
 
     kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker,
     kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord,
@@ -826,9 +825,8 @@
   };
 
   // This is a thin wrapper around the thread-tracker's ScopedActivity that
-  // accesses the global tracker to provide some of the information, notably
-  // which thread-tracker to use. It is safe to create even if activity
-  // tracking is not enabled.
+  // allows thread-safe access to data values. It is safe to use even if
+  // activity tracking is not enabled.
   class BASE_EXPORT ScopedThreadActivity
       : public ThreadActivityTracker::ScopedActivity {
    public:
@@ -1032,10 +1030,6 @@
   // Updates to this are thread-safe.
   ActivityUserData& process_data() { return process_data_; }
 
-  // Accesses the global data record for storing arbitrary key/value pairs.
-  // Updates to this are thread-safe.
-  ActivityUserData& global_data() { return global_data_; }
-
  private:
   friend class GlobalActivityAnalyzer;
   friend class ScopedThreadActivity;
@@ -1190,7 +1184,6 @@
 
   // An object for holding arbitrary key value pairs with thread-safe access.
   ThreadSafeUserData process_data_;
-  ThreadSafeUserData global_data_;
 
   // A map of global module information, keyed by module path.
   std::map<const std::string, ModuleInfoRecord*> modules_;
diff --git a/base/debug/activity_tracker_unittest.cc b/base/debug/activity_tracker_unittest.cc
index c7efa58..372d6ac 100644
--- a/base/debug/activity_tracker_unittest.cc
+++ b/base/debug/activity_tracker_unittest.cc
@@ -216,7 +216,7 @@
   ASSERT_EQ(0U, snapshot.activity_stack.size());
 
   {
-    PendingTask task1(FROM_HERE, base::Bind(&DoNothing));
+    PendingTask task1(FROM_HERE, base::BindOnce(&DoNothing));
     ScopedTaskRunActivity activity1(task1);
     ActivityUserData& user_data1 = activity1.user_data();
     (void)user_data1;  // Tell compiler it's been used.
@@ -227,7 +227,7 @@
     EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type);
 
     {
-      PendingTask task2(FROM_HERE, base::Bind(&DoNothing));
+      PendingTask task2(FROM_HERE, base::BindOnce(&DoNothing));
       ScopedTaskRunActivity activity2(task2);
       ActivityUserData& user_data2 = activity2.user_data();
       (void)user_data2;  // Tell compiler it's been used.
@@ -306,8 +306,6 @@
 
   // Ensure the data repositories have backing store, indicated by non-zero ID.
   EXPECT_NE(0U, global->process_data().id());
-  EXPECT_NE(0U, global->global_data().id());
-  EXPECT_NE(global->process_data().id(), global->global_data().id());
 }
 
 class SimpleActivityThread : public SimpleThread {
@@ -413,7 +411,7 @@
   global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar"));
 
   // Do some activities.
-  PendingTask task(FROM_HERE, base::Bind(&DoNothing));
+  PendingTask task(FROM_HERE, base::BindOnce(&DoNothing));
   ScopedTaskRunActivity activity(task);
   ActivityUserData& user_data = activity.user_data();
   ASSERT_NE(0U, user_data.id());
diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc
index 8a1c8bdc..bfb0e7c 100644
--- a/base/debug/task_annotator_unittest.cc
+++ b/base/debug/task_annotator_unittest.cc
@@ -19,7 +19,7 @@
 
 TEST(TaskAnnotatorTest, QueueAndRunTask) {
   int result = 0;
-  PendingTask pending_task(FROM_HERE, Bind(&TestTask, &result));
+  PendingTask pending_task(FROM_HERE, BindOnce(&TestTask, &result));
 
   TaskAnnotator annotator;
   annotator.DidQueueTask("TaskAnnotatorTest::Queue", pending_task);
diff --git a/base/deferred_sequenced_task_runner_unittest.cc b/base/deferred_sequenced_task_runner_unittest.cc
index e34827a..a39b2d3 100644
--- a/base/deferred_sequenced_task_runner_unittest.cc
+++ b/base/deferred_sequenced_task_runner_unittest.cc
@@ -44,10 +44,9 @@
   }
 
   void PostExecuteTask(int task_id) {
-    runner_->PostTask(FROM_HERE,
-                      base::Bind(&DeferredSequencedTaskRunnerTest::ExecuteTask,
-                                 base::Unretained(this),
-                                 task_id));
+    runner_->PostTask(
+        FROM_HERE, base::BindOnce(&DeferredSequencedTaskRunnerTest::ExecuteTask,
+                                  base::Unretained(this), task_id));
   }
 
   void StartRunner() {
@@ -126,16 +125,17 @@
     for (int i = 0; i < 5; ++i) {
       thread1.task_runner()->PostTask(
           FROM_HERE,
-          base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
-                     base::Unretained(this), 2 * i));
+          base::BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
+                         base::Unretained(this), 2 * i));
       thread2.task_runner()->PostTask(
           FROM_HERE,
-          base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
-                     base::Unretained(this), 2 * i + 1));
+          base::BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
+                         base::Unretained(this), 2 * i + 1));
       if (i == 2) {
         thread1.task_runner()->PostTask(
-            FROM_HERE, base::Bind(&DeferredSequencedTaskRunnerTest::StartRunner,
-                                  base::Unretained(this)));
+            FROM_HERE,
+            base::BindOnce(&DeferredSequencedTaskRunnerTest::StartRunner,
+                           base::Unretained(this)));
       }
     }
   }
@@ -157,9 +157,10 @@
         scoped_refptr<ExecuteTaskOnDestructor> short_lived_object =
             new ExecuteTaskOnDestructor(this, 2 * i);
         runner_->PostTask(
-            FROM_HERE, base::Bind(&DeferredSequencedTaskRunnerTest::DoNothing,
-                                  base::Unretained(this),
-                                  base::RetainedRef(short_lived_object)));
+            FROM_HERE,
+            base::BindOnce(&DeferredSequencedTaskRunnerTest::DoNothing,
+                           base::Unretained(this),
+                           base::RetainedRef(short_lived_object)));
       }
       // |short_lived_object| with id |2 * i| should be destroyed before the
       // task |2 * i + 1| is executed.
diff --git a/base/files/file_descriptor_watcher_posix.cc b/base/files/file_descriptor_watcher_posix.cc
index 9746e35e..ce05081 100644
--- a/base/files/file_descriptor_watcher_posix.cc
+++ b/base/files/file_descriptor_watcher_posix.cc
@@ -124,8 +124,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Run the callback on the sequence on which the watch was initiated.
-  callback_task_runner_->PostTask(FROM_HERE,
-                                  Bind(&Controller::RunCallback, controller_));
+  callback_task_runner_->PostTask(
+      FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
 }
 
 void FileDescriptorWatcher::Controller::Watcher::OnFileCanWriteWithoutBlocking(
@@ -135,8 +135,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Run the callback on the sequence on which the watch was initiated.
-  callback_task_runner_->PostTask(FROM_HERE,
-                                  Bind(&Controller::RunCallback, controller_));
+  callback_task_runner_->PostTask(
+      FROM_HERE, BindOnce(&Controller::RunCallback, controller_));
 }
 
 void FileDescriptorWatcher::Controller::Watcher::
@@ -170,7 +170,7 @@
   // Controller's destructor. Since this delete task hasn't been posted yet, it
   // can't run before the task posted below.
   message_loop_for_io_task_runner_->PostTask(
-      FROM_HERE, Bind(&Watcher::StartWatching, Unretained(watcher_.get())));
+      FROM_HERE, BindOnce(&Watcher::StartWatching, Unretained(watcher_.get())));
 }
 
 void FileDescriptorWatcher::Controller::RunCallback() {
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc
index 1dc833d..f02a8ea6 100644
--- a/base/files/file_path_watcher_linux.cc
+++ b/base/files/file_path_watcher_linux.cc
@@ -255,8 +255,7 @@
 
   if (inotify_fd_ >= 0 && thread_.Start()) {
     thread_.task_runner()->PostTask(
-        FROM_HERE,
-        Bind(&InotifyReaderCallback, this, inotify_fd_));
+        FROM_HERE, BindOnce(&InotifyReaderCallback, this, inotify_fd_));
     valid_ = true;
   }
 }
@@ -331,9 +330,10 @@
   // access |watches_| safely. Use a WeakPtr to prevent the callback from
   // running after |this| is destroyed (i.e. after the watch is cancelled).
   task_runner()->PostTask(
-      FROM_HERE, Bind(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence,
-                      weak_factory_.GetWeakPtr(), fired_watch, child, created,
-                      deleted, is_dir));
+      FROM_HERE,
+      BindOnce(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence,
+               weak_factory_.GetWeakPtr(), fired_watch, child, created, deleted,
+               is_dir));
 }
 
 void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence(
diff --git a/base/files/file_path_watcher_unittest.cc b/base/files/file_path_watcher_unittest.cc
index d2ec37b..dcc6d5f 100644
--- a/base/files/file_path_watcher_unittest.cc
+++ b/base/files/file_path_watcher_unittest.cc
@@ -56,8 +56,8 @@
   // Called from the file thread by the delegates.
   void OnChange(TestDelegate* delegate) {
     task_runner_->PostTask(
-        FROM_HERE, base::Bind(&NotificationCollector::RecordChange, this,
-                              base::Unretained(delegate)));
+        FROM_HERE, base::BindOnce(&NotificationCollector::RecordChange, this,
+                                  base::Unretained(delegate)));
   }
 
   void Register(TestDelegate* delegate) {
diff --git a/base/files/file_proxy.cc b/base/files/file_proxy.cc
index 01933d0..a7950f00 100644
--- a/base/files/file_proxy.cc
+++ b/base/files/file_proxy.cc
@@ -37,7 +37,8 @@
      if (proxy_)
        proxy_->SetFile(std::move(file_));
      else if (file_.IsValid())
-       task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
+       task_runner_->PostTask(FROM_HERE,
+                              BindOnce(&FileDeleter, Passed(&file_)));
    }
 
  protected:
@@ -235,7 +236,7 @@
 
 FileProxy::~FileProxy() {
   if (file_.IsValid())
-    task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
+    task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, Passed(&file_)));
 }
 
 bool FileProxy::CreateOrOpen(const FilePath& file_path,
@@ -245,9 +246,9 @@
   CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
   return task_runner_->PostTaskAndReply(
       FROM_HERE,
-      Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
-           file_flags),
-      Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback));
+      BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
+               file_flags),
+      BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::CreateTemporary(uint32_t additional_file_flags,
@@ -256,9 +257,9 @@
   CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
   return task_runner_->PostTaskAndReply(
       FROM_HERE,
-      Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
-           additional_file_flags),
-      Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
+      BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper),
+               additional_file_flags),
+      BindOnce(&CreateTemporaryHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::IsValid() const {
@@ -286,18 +287,16 @@
   DCHECK(file_.IsValid());
   GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
   return task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      Bind(&GenericFileHelper::Close, Unretained(helper)),
-      Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+      FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::GetInfo(const GetFileInfoCallback& callback) {
   DCHECK(file_.IsValid());
   GetInfoHelper* helper = new GetInfoHelper(this, std::move(file_));
   return task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      Bind(&GetInfoHelper::RunWork, Unretained(helper)),
-      Bind(&GetInfoHelper::Reply, Owned(helper), callback));
+      FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)),
+      BindOnce(&GetInfoHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::Read(int64_t offset,
@@ -309,9 +308,8 @@
 
   ReadHelper* helper = new ReadHelper(this, std::move(file_), bytes_to_read);
   return task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      Bind(&ReadHelper::RunWork, Unretained(helper), offset),
-      Bind(&ReadHelper::Reply, Owned(helper), callback));
+      FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset),
+      BindOnce(&ReadHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::Write(int64_t offset,
@@ -325,9 +323,8 @@
   WriteHelper* helper =
       new WriteHelper(this, std::move(file_), buffer, bytes_to_write);
   return task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      Bind(&WriteHelper::RunWork, Unretained(helper), offset),
-      Bind(&WriteHelper::Reply, Owned(helper), callback));
+      FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset),
+      BindOnce(&WriteHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::SetTimes(Time last_access_time,
@@ -337,9 +334,9 @@
   GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
   return task_runner_->PostTaskAndReply(
       FROM_HERE,
-      Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time,
-           last_modified_time),
-      Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+      BindOnce(&GenericFileHelper::SetTimes, Unretained(helper),
+               last_access_time, last_modified_time),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::SetLength(int64_t length, const StatusCallback& callback) {
@@ -347,17 +344,16 @@
   GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
   return task_runner_->PostTaskAndReply(
       FROM_HERE,
-      Bind(&GenericFileHelper::SetLength, Unretained(helper), length),
-      Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+      BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), callback));
 }
 
 bool FileProxy::Flush(const StatusCallback& callback) {
   DCHECK(file_.IsValid());
   GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
   return task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      Bind(&GenericFileHelper::Flush, Unretained(helper)),
-      Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+      FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), callback));
 }
 
 }  // namespace base
diff --git a/base/files/file_util_proxy.cc b/base/files/file_util_proxy.cc
index 54a5661..dc39a65 100644
--- a/base/files/file_util_proxy.cc
+++ b/base/files/file_util_proxy.cc
@@ -59,9 +59,9 @@
   GetFileInfoHelper* helper = new GetFileInfoHelper;
   return task_runner->PostTaskAndReply(
       FROM_HERE,
-      Bind(&GetFileInfoHelper::RunWorkForFilePath,
-           Unretained(helper), file_path),
-      Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
+      BindOnce(&GetFileInfoHelper::RunWorkForFilePath, Unretained(helper),
+               file_path),
+      BindOnce(&GetFileInfoHelper::Reply, Owned(helper), callback));
 }
 
 // static
diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc
index 9b8dcfd..88ad352 100644
--- a/base/files/important_file_writer_unittest.cc
+++ b/base/files/important_file_writer_unittest.cc
@@ -201,8 +201,8 @@
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
   file_writer_thread.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&wait_helper)));
+      FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait,
+                                base::Unretained(&wait_helper)));
 
   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
   writer.WriteNow(MakeUnique<std::string>("foo"));
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc
index 07b9d509..19f28f21 100644
--- a/base/json/json_writer.cc
+++ b/base/json/json_writer.cc
@@ -128,7 +128,7 @@
       bool result = node.GetAsList(&list);
       DCHECK(result);
       for (const auto& value : *list) {
-        if (omit_binary_values_ && value->GetType() == Value::Type::BINARY)
+        if (omit_binary_values_ && value.GetType() == Value::Type::BINARY)
           continue;
 
         if (first_value_has_been_output) {
@@ -137,7 +137,7 @@
             json_string_->push_back(' ');
         }
 
-        if (!BuildJSONString(*value, depth))
+        if (!BuildJSONString(value, depth))
           result = false;
 
         first_value_has_been_output = true;
diff --git a/base/memory/weak_ptr_unittest.cc b/base/memory/weak_ptr_unittest.cc
index 1a4870e..d223bd2b 100644
--- a/base/memory/weak_ptr_unittest.cc
+++ b/base/memory/weak_ptr_unittest.cc
@@ -32,7 +32,8 @@
       Thread creator_thread("creator_thread");
       creator_thread.Start();
       creator_thread.task_runner()->PostTask(
-          FROM_HERE, base::Bind(OffThreadObjectCreator::CreateObject, &result));
+          FROM_HERE,
+          base::BindOnce(OffThreadObjectCreator::CreateObject, &result));
     }
     DCHECK(result);  // We synchronized on thread destruction above.
     return result;
@@ -73,8 +74,8 @@
     WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
-        FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromTarget, arrow,
-                              target, &completion));
+        FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromTarget,
+                                  arrow, target, &completion));
     completion.Wait();
   }
 
@@ -82,8 +83,8 @@
     WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
-        FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromArrow, arrow,
-                              other, &completion));
+        FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromArrow,
+                                  arrow, other, &completion));
     completion.Wait();
   }
 
@@ -92,7 +93,7 @@
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion));
+        base::BindOnce(&BackgroundThread::DoDeleteTarget, object, &completion));
     completion.Wait();
   }
 
@@ -100,8 +101,8 @@
     WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
-        FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrow, object,
-                              &completion));
+        FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrow,
+                                  object, &completion));
     completion.Wait();
   }
 
@@ -109,8 +110,8 @@
     WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
-        FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrowBase,
-                              object, &completion));
+        FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrowBase,
+                                  object, &completion));
     completion.Wait();
   }
 
@@ -119,7 +120,7 @@
                              WaitableEvent::InitialState::NOT_SIGNALED);
     task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion));
+        base::BindOnce(&BackgroundThread::DoDeleteArrow, object, &completion));
     completion.Wait();
   }
 
@@ -127,8 +128,9 @@
     WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
     Target* result = nullptr;
-    task_runner()->PostTask(FROM_HERE, base::Bind(&BackgroundThread::DoDeRef,
-                                                  arrow, &result, &completion));
+    task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&BackgroundThread::DoDeRef, arrow, &result,
+                                  &completion));
     completion.Wait();
     return result;
   }
diff --git a/base/message_loop/message_loop_task_runner_unittest.cc b/base/message_loop/message_loop_task_runner_unittest.cc
index d403c70..5fa01f0 100644
--- a/base/message_loop/message_loop_task_runner_unittest.cc
+++ b/base/message_loop/message_loop_task_runner_unittest.cc
@@ -38,8 +38,8 @@
 
     // Allow us to pause the |task_thread_|'s MessageLoop.
     task_thread_.task_runner()->PostTask(
-        FROM_HERE, Bind(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper,
-                        Unretained(this)));
+        FROM_HERE, BindOnce(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper,
+                            Unretained(this)));
   }
 
   void TearDown() override {
@@ -114,8 +114,8 @@
       new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
 
   ASSERT_TRUE(task_thread_.task_runner()->PostTaskAndReply(
-      FROM_HERE, Bind(&RecordLoop, task_recorder),
-      Bind(&RecordLoopAndQuit, reply_recorder)));
+      FROM_HERE, BindOnce(&RecordLoop, task_recorder),
+      BindOnce(&RecordLoopAndQuit, reply_recorder)));
 
   // Die if base::Bind doesn't retain a reference to the recorders.
   task_recorder = NULL;
@@ -152,9 +152,9 @@
   UnblockTaskThread();
   task_thread_.Stop();
 
-  ASSERT_FALSE(
-      task_runner->PostTaskAndReply(FROM_HERE, Bind(&RecordLoop, task_recorder),
-                                    Bind(&RecordLoopAndQuit, reply_recorder)));
+  ASSERT_FALSE(task_runner->PostTaskAndReply(
+      FROM_HERE, BindOnce(&RecordLoop, task_recorder),
+      BindOnce(&RecordLoopAndQuit, reply_recorder)));
 
   // The relay should have properly deleted its resources leaving us as the only
   // reference.
@@ -182,8 +182,8 @@
 
   // Enqueue the relay.
   ASSERT_TRUE(current_loop_->task_runner()->PostTaskAndReply(
-      FROM_HERE, Bind(&RecordLoop, task_recorder),
-      Bind(&RecordLoopAndQuit, reply_recorder)));
+      FROM_HERE, BindOnce(&RecordLoop, task_recorder),
+      BindOnce(&RecordLoopAndQuit, reply_recorder)));
 
   // Die if base::Bind doesn't retain a reference to the recorders.
   task_recorder = NULL;
@@ -218,8 +218,8 @@
 
   // Enqueue the relay.
   task_thread_.task_runner()->PostTaskAndReply(
-      FROM_HERE, Bind(&RecordLoop, task_recorder),
-      Bind(&RecordLoopAndQuit, reply_recorder));
+      FROM_HERE, BindOnce(&RecordLoop, task_recorder),
+      BindOnce(&RecordLoopAndQuit, reply_recorder));
 
   // Die if base::Bind doesn't retain a reference to the recorders.
   task_recorder = NULL;
@@ -331,8 +331,8 @@
 
 TEST_F(MessageLoopTaskRunnerThreadingTest, PostTask) {
   EXPECT_TRUE(file_thread_->task_runner()->PostTask(
-      FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::BasicFunction,
-                      Unretained(this))));
+      FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::BasicFunction,
+                          Unretained(this))));
   RunLoop().Run();
 }
 
@@ -345,7 +345,7 @@
   test_thread->Stop();
 
   bool ret = task_runner->PostTask(
-      FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
+      FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
   EXPECT_FALSE(ret);
 }
 
@@ -358,7 +358,7 @@
     task_runner = test_thread->task_runner();
   }
   bool ret = task_runner->PostTask(
-      FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
+      FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
   EXPECT_FALSE(ret);
 }
 
diff --git a/base/message_loop/message_loop_test.cc b/base/message_loop/message_loop_test.cc
index 6ffb16d0..ad9f127 100644
--- a/base/message_loop/message_loop_test.cc
+++ b/base/message_loop/message_loop_test.cc
@@ -98,21 +98,22 @@
   // Add tests to message loop
   scoped_refptr<Foo> foo(new Foo());
   std::string a("a"), b("b"), c("c"), d("d");
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&Foo::Test0, foo));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&Foo::Test1ConstRef, foo, a));
+                                          BindOnce(&Foo::Test0, foo));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&Foo::Test1Ptr, foo, &b));
+                                          BindOnce(&Foo::Test1Ptr, foo, &b));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&Foo::Test1Int, foo, 100));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&Foo::Test2Ptr, foo, &a, &c));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&Foo::Test2Mixed, foo, a, &d));
+                                          BindOnce(&Foo::Test1Int, foo, 100));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d));
   // After all tests, post a message that will shut down the message loop
   ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      Bind(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
+      BindOnce(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
 
   // Now kick things off
   RunLoop().Run();
@@ -133,7 +134,7 @@
   Time run_time;
 
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay);
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay);
 
   Time time_before_run = Time::Now();
   RunLoop().Run();
@@ -152,12 +153,12 @@
   Time run_time1, run_time2;
 
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
       TimeDelta::FromMilliseconds(200));
   // If we get a large pause in execution (due to a context switch) here, this
   // test could fail.
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
       TimeDelta::FromMilliseconds(10));
 
   RunLoop().Run();
@@ -184,9 +185,9 @@
   Time run_time1, run_time2;
 
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
 
   RunLoop().Run();
   EXPECT_EQ(0, num_tasks);
@@ -206,9 +207,10 @@
   int num_tasks = 2;
   Time run_time;
 
-  loop.task_runner()->PostTask(FROM_HERE, Bind(&SlowFunc, kPause, &num_tasks));
+  loop.task_runner()->PostTask(FROM_HERE,
+                               BindOnce(&SlowFunc, kPause, &num_tasks));
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks),
       TimeDelta::FromMilliseconds(10));
 
   Time time_before_run = Time::Now();
@@ -236,10 +238,10 @@
   // Clutter the ML with tasks.
   for (int i = 1; i < num_tasks; ++i)
     loop.task_runner()->PostTask(
-        FROM_HERE, Bind(&RecordRunTimeFunc, &run_time1, &num_tasks));
+        FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks));
 
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
       TimeDelta::FromMilliseconds(1));
 
   RunLoop().Run();
@@ -261,10 +263,10 @@
   Time run_time1, run_time2;
 
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
       TimeDelta::FromSeconds(1000));
   loop.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+      FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
       TimeDelta::FromMilliseconds(10));
 
   Time start_time = Time::Now();
@@ -303,7 +305,7 @@
     *was_deleted_ = true;
     if (post_on_delete_.get())
       ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_));
+          FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_));
   }
 
   scoped_refptr<RecordDeletionProbe> post_on_delete_;
@@ -317,12 +319,13 @@
     std::unique_ptr<MessagePump> pump(factory());
     MessageLoop loop(std::move(pump));
     loop.task_runner()->PostTask(
-        FROM_HERE, Bind(&RecordDeletionProbe::Run,
-                        new RecordDeletionProbe(NULL, &a_was_deleted)));
+        FROM_HERE, BindOnce(&RecordDeletionProbe::Run,
+                            new RecordDeletionProbe(NULL, &a_was_deleted)));
     // TODO(ajwong): Do we really need 1000ms here?
     loop.task_runner()->PostDelayedTask(
-        FROM_HERE, Bind(&RecordDeletionProbe::Run,
-                        new RecordDeletionProbe(NULL, &b_was_deleted)),
+        FROM_HERE,
+        BindOnce(&RecordDeletionProbe::Run,
+                 new RecordDeletionProbe(NULL, &b_was_deleted)),
         TimeDelta::FromMilliseconds(1000));
   }
   EXPECT_TRUE(a_was_deleted);
@@ -341,7 +344,8 @@
     RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
     RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
     RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
-    loop.task_runner()->PostTask(FROM_HERE, Bind(&RecordDeletionProbe::Run, c));
+    loop.task_runner()->PostTask(FROM_HERE,
+                                 BindOnce(&RecordDeletionProbe::Run, c));
   }
   EXPECT_TRUE(a_was_deleted);
   EXPECT_TRUE(b_was_deleted);
@@ -352,7 +356,7 @@
   if (*depth > 0) {
     *depth -= 1;
     ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                            Bind(&NestingFunc, depth));
+                                            BindOnce(&NestingFunc, depth));
 
     MessageLoop::current()->SetNestableTasksAllowed(true);
     RunLoop().Run();
@@ -366,7 +370,7 @@
 
   int depth = 100;
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&NestingFunc, &depth));
+                                          BindOnce(&NestingFunc, &depth));
   RunLoop().Run();
   EXPECT_EQ(depth, 0);
 }
@@ -404,7 +408,7 @@
   // Verify that by the time the first task is run the observer has seen the
   // message loop begin.
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&ExpectOneBeginNestedLoop, observer));
+      FROM_HERE, BindOnce(&ExpectOneBeginNestedLoop, observer));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, nested_loop.QuitClosure());
   nested_loop.Run();
 
@@ -424,9 +428,9 @@
   outer_loop.AddNestingObserver(&nesting_observer);
 
   // Post a task that runs a nested message loop.
-  outer_loop.task_runner()->PostTask(FROM_HERE,
-                                     Bind(&RunNestedLoop, &nesting_observer,
-                                          outer_loop.QuitWhenIdleClosure()));
+  outer_loop.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&RunNestedLoop, &nesting_observer,
+                          outer_loop.QuitWhenIdleClosure()));
   RunLoop().Run();
 
   outer_loop.RemoveNestingObserver(&nesting_observer);
@@ -519,7 +523,7 @@
       MessageLoop::current()->SetNestableTasksAllowed(true);
     ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
+        BindOnce(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
   }
   order->RecordEnd(RECURSIVE, cookie);
 }
@@ -536,11 +540,11 @@
   EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
   TaskList order;
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, false));
+      FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, false));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, false));
+      FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, false));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&QuitFunc, &order, 3));
+                                          BindOnce(&QuitFunc, &order, 3));
 
   RunLoop().Run();
 
@@ -580,13 +584,15 @@
   EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
   TaskList order;
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false));
+      FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 1, 2, false));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false));
+      FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 2, 2, false));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 3), TimeDelta::FromMilliseconds(5));
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 3),
+      TimeDelta::FromMilliseconds(5));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&QuitFunc, &order, 4), TimeDelta::FromMilliseconds(5));
+      FROM_HERE, BindOnce(&QuitFunc, &order, 4),
+      TimeDelta::FromMilliseconds(5));
 
   RunLoop().Run();
 
@@ -616,11 +622,11 @@
 
   TaskList order;
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true));
+      FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, true));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true));
+      FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, true));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&QuitFunc, &order, 3));
+                                          BindOnce(&QuitFunc, &order, 3));
 
   RunLoop().Run();
 
@@ -650,11 +656,11 @@
   TaskList order;
 
   ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 1));
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&QuitFunc, &order, 3));
+                                          BindOnce(&QuitFunc, &order, 3));
   RunLoop().Run();
 
   // FIFO order.
@@ -690,17 +696,18 @@
   TaskList order;
 
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&FuncThatPumps, &order, 1));
+                                          BindOnce(&FuncThatPumps, &order, 1));
   ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 2));
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 3));
+                                          BindOnce(&OrderedFunc, &order, 3));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
+      FROM_HERE,
+      BindOnce(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 5));
+                                          BindOnce(&OrderedFunc, &order, 5));
   ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
-      FROM_HERE, Bind(&QuitFunc, &order, 6));
+      FROM_HERE, BindOnce(&QuitFunc, &order, 6));
 
   RunLoop().Run();
 
@@ -742,15 +749,17 @@
   RunLoop run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 3));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
+                                          BindOnce(&FuncThatQuitsNow));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          BindOnce(&OrderedFunc, &order, 3));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          BindOnce(&FuncThatQuitsNow));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 4));  // never runs
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 4));  // never runs
 
   RunLoop().Run();
 
@@ -776,11 +785,12 @@
   RunLoop nested_run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+      FROM_HERE,
+      BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           outer_run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_run_loop.QuitClosure());
 
@@ -806,11 +816,12 @@
   RunLoop nested_run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+      FROM_HERE,
+      BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           outer_run_loop.QuitClosure());
 
@@ -837,11 +848,12 @@
   RunLoop bogus_run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+      FROM_HERE,
+      BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           bogus_run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           outer_run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
@@ -872,35 +884,35 @@
   RunLoop nested_loop4;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 5));
+                                          BindOnce(&OrderedFunc, &order, 5));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           outer_run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 6));
+                                          BindOnce(&OrderedFunc, &order, 6));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_loop1.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 7));
+                                          BindOnce(&OrderedFunc, &order, 7));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_loop2.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 8));
+                                          BindOnce(&OrderedFunc, &order, 8));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_loop3.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 9));
+                                          BindOnce(&OrderedFunc, &order, 9));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                           nested_loop4.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 10));
+                                          BindOnce(&OrderedFunc, &order, 10));
 
   outer_run_loop.Run();
 
@@ -939,9 +951,9 @@
   run_loop.Quit();
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 1));  // never runs
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 1));  // never runs
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatQuitsNow));  // never runs
+      FROM_HERE, BindOnce(&FuncThatQuitsNow));  // never runs
 
   run_loop.Run();
 
@@ -958,12 +970,12 @@
   RunLoop run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 1));
+                                          BindOnce(&OrderedFunc, &order, 1));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&OrderedFunc, &order, 2));  // never runs
+      FROM_HERE, BindOnce(&OrderedFunc, &order, 2));  // never runs
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatQuitsNow));  // never runs
+      FROM_HERE, BindOnce(&FuncThatQuitsNow));  // never runs
 
   run_loop.Run();
 
@@ -984,17 +996,19 @@
   RunLoop run_loop;
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+      FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 2));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
+                                          BindOnce(&OrderedFunc, &order, 2));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 3));
+                                          BindOnce(&FuncThatQuitsNow));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          BindOnce(&OrderedFunc, &order, 3));
   ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, run_loop.QuitClosure());  // has no affect
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&OrderedFunc, &order, 4));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FuncThatQuitsNow));
+                                          BindOnce(&OrderedFunc, &order, 4));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          BindOnce(&FuncThatQuitsNow));
 
   RunLoop outer_run_loop;
   outer_run_loop.Run();
@@ -1015,7 +1029,7 @@
 void PostNTasksThenQuit(int posts_remaining) {
   if (posts_remaining > 1) {
     ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, Bind(&PostNTasksThenQuit, posts_remaining - 1));
+        FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
   } else {
     MessageLoop::current()->QuitWhenIdle();
   }
@@ -1033,7 +1047,8 @@
   const int kNumTimes = 1 << 17;
   std::unique_ptr<MessagePump> pump(factory());
   MessageLoop loop(std::move(pump));
-  loop.task_runner()->PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumTimes));
+  loop.task_runner()->PostTask(FROM_HERE,
+                               BindOnce(&PostNTasksThenQuit, kNumTimes));
   RunLoop().Run();
 }
 
diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc
index 9d771d5ec..fe3c015f 100644
--- a/base/message_loop/message_loop_unittest.cc
+++ b/base/message_loop/message_loop_unittest.cc
@@ -470,7 +470,7 @@
 void PostNTasksThenQuit(int posts_remaining) {
   if (posts_remaining > 1) {
     ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, Bind(&PostNTasksThenQuit, posts_remaining - 1));
+        FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
   } else {
     MessageLoop::current()->QuitWhenIdle();
   }
@@ -682,7 +682,8 @@
 
   MessageLoop loop;
   loop.AddTaskObserver(&observer);
-  loop.task_runner()->PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumPosts));
+  loop.task_runner()->PostTask(FROM_HERE,
+                               BindOnce(&PostNTasksThenQuit, kNumPosts));
   RunLoop().Run();
   loop.RemoveTaskObserver(&observer);
 
@@ -859,9 +860,10 @@
   MLDestructionObserver observer(&task_destroyed, &destruction_observer_called);
   loop->AddDestructionObserver(&observer);
   loop->task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&DestructionObserverProbe::Run,
-                      new DestructionObserverProbe(
-                          &task_destroyed, &destruction_observer_called)),
+      FROM_HERE,
+      BindOnce(&DestructionObserverProbe::Run,
+               new DestructionObserverProbe(&task_destroyed,
+                                            &destruction_observer_called)),
       kDelay);
   delete loop;
   EXPECT_TRUE(observer.task_destroyed_before_message_loop());
@@ -878,13 +880,13 @@
 
   scoped_refptr<Foo> foo(new Foo());
   std::string a("a");
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(
-      &Foo::Test1ConstRef, foo, a));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
 
   // Post quit task;
   ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      Bind(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
+      BindOnce(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
 
   // Now kick things off
   RunLoop().Run();
@@ -1007,8 +1009,7 @@
   loop.SetTaskRunner(new_runner);
 
   scoped_refptr<Foo> foo(new Foo());
-  original_runner->PostTask(FROM_HERE,
-                            Bind(&Foo::Test1ConstRef, foo, "a"));
+  original_runner->PostTask(FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, "a"));
   RunLoop().RunUntilIdle();
   EXPECT_EQ(1, foo->test_count());
 }
diff --git a/base/message_loop/message_pump_glib_unittest.cc b/base/message_loop/message_pump_glib_unittest.cc
index 607d3c93..bef0c8f 100644
--- a/base/message_loop/message_pump_glib_unittest.cc
+++ b/base/message_loop/message_pump_glib_unittest.cc
@@ -237,13 +237,13 @@
   // The event queue is empty at first.
   for (int i = 0; i < 10; ++i) {
     loop()->task_runner()->PostTask(FROM_HERE,
-                                    Bind(&IncrementInt, &task_count));
+                                    BindOnce(&IncrementInt, &task_count));
   }
   // After all the previous tasks have executed, enqueue an event that will
   // quit.
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&EventInjector::AddEvent, Unretained(injector()), 0,
-                      MessageLoop::QuitWhenIdleClosure()));
+      FROM_HERE, BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
+                          MessageLoop::QuitWhenIdleClosure()));
   RunLoop().Run();
   ASSERT_EQ(10, task_count);
   EXPECT_EQ(1, injector()->processed_events());
@@ -253,7 +253,7 @@
   task_count = 0;
   for (int i = 0; i < 10; ++i) {
     loop()->task_runner()->PostDelayedTask(FROM_HERE,
-                                           Bind(&IncrementInt, &task_count),
+                                           BindOnce(&IncrementInt, &task_count),
                                            TimeDelta::FromMilliseconds(10 * i));
   }
   // After all the previous tasks have executed, enqueue an event that will
@@ -261,8 +261,9 @@
   // This relies on the fact that delayed tasks are executed in delay order.
   // That is verified in message_loop_unittest.cc.
   loop()->task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&EventInjector::AddEvent, Unretained(injector()), 10,
-                      MessageLoop::QuitWhenIdleClosure()),
+      FROM_HERE,
+      BindOnce(&EventInjector::AddEvent, Unretained(injector()), 10,
+               MessageLoop::QuitWhenIdleClosure()),
       TimeDelta::FromMilliseconds(150));
   RunLoop().Run();
   ASSERT_EQ(10, task_count);
@@ -312,7 +313,7 @@
         MessageLoop::current()->QuitWhenIdle();
     } else {
       ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, Bind(&ConcurrentHelper::FromTask, this));
+          FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
     }
   }
 
@@ -363,9 +364,9 @@
 
   // Similarly post 2 tasks.
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper));
+      FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper));
+      FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
 
   RunLoop().Run();
   EXPECT_EQ(0, helper->event_count());
@@ -382,8 +383,8 @@
   injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
 
   // Post a couple of dummy tasks
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&DoNothing));
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&DoNothing));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, BindOnce(&DoNothing));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, BindOnce(&DoNothing));
 
   // Drain the events
   while (g_main_context_pending(NULL)) {
@@ -396,7 +397,7 @@
 TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
   // Tests that draining events using GLib works.
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&AddEventsAndDrainGLib, Unretained(injector())));
+      FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector())));
   RunLoop().Run();
 
   EXPECT_EQ(3, injector()->processed_events());
@@ -448,18 +449,18 @@
   injector->AddDummyEvent(0);
   // Post a couple of dummy tasks
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&IncrementInt, &task_count));
+                                          BindOnce(&IncrementInt, &task_count));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&IncrementInt, &task_count));
+                                          BindOnce(&IncrementInt, &task_count));
   // Delayed events
   injector->AddDummyEvent(10);
   injector->AddDummyEvent(10);
   // Delayed work
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&IncrementInt, &task_count),
+      FROM_HERE, BindOnce(&IncrementInt, &task_count),
       TimeDelta::FromMilliseconds(30));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&GLibLoopRunner::Quit, runner),
+      FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
       TimeDelta::FromMilliseconds(40));
 
   // Run a nested, straight GLib message loop.
@@ -481,18 +482,18 @@
   injector->AddDummyEvent(0);
   // Post a couple of dummy tasks
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&IncrementInt, &task_count));
+                                          BindOnce(&IncrementInt, &task_count));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                          Bind(&IncrementInt, &task_count));
+                                          BindOnce(&IncrementInt, &task_count));
   // Delayed events
   injector->AddDummyEvent(10);
   injector->AddDummyEvent(10);
   // Delayed work
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&IncrementInt, &task_count),
+      FROM_HERE, BindOnce(&IncrementInt, &task_count),
       TimeDelta::FromMilliseconds(30));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&GLibLoopRunner::Quit, runner),
+      FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
       TimeDelta::FromMilliseconds(40));
 
   // Run a nested, straight Gtk message loop.
@@ -511,7 +512,7 @@
   // Note that in this case we don't make strong guarantees about niceness
   // between events and posted tasks.
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&TestGLibLoopInternal, Unretained(injector())));
+      FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector())));
   RunLoop().Run();
 }
 
@@ -521,7 +522,7 @@
   // Note that in this case we don't make strong guarantees about niceness
   // between events and posted tasks.
   loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(&TestGtkLoopInternal, Unretained(injector())));
+      FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector())));
   RunLoop().Run();
 }
 
diff --git a/base/message_loop/message_pump_libevent_unittest.cc b/base/message_loop/message_pump_libevent_unittest.cc
index 3e7a200a..0a7c485 100644
--- a/base/message_loop/message_pump_libevent_unittest.cc
+++ b/base/message_loop/message_pump_libevent_unittest.cc
@@ -187,7 +187,7 @@
   void OnFileCanReadWithoutBlocking(int /* fd */) override {
     RunLoop runloop;
     ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, Bind(&QuitMessageLoopAndStart, runloop.QuitClosure()));
+        FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure()));
     runloop.Run();
   }
 
@@ -218,7 +218,7 @@
 
   void OnFileCanReadWithoutBlocking(int /* fd */) override {
     // Post a fatal closure to the MessageLoop before we quit it.
-    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&FatalClosure));
+    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, BindOnce(&FatalClosure));
 
     // Now quit the MessageLoop.
     run_loop_->Quit();
@@ -259,12 +259,12 @@
   const WaitableEventWatcher::EventCallback write_fd_task =
       Bind(&WriteFDWrapper, pipefds_[1], &buf, 1);
   io_loop()->task_runner()->PostTask(
-      FROM_HERE, Bind(IgnoreResult(&WaitableEventWatcher::StartWatching),
-                      Unretained(watcher.get()), &event, write_fd_task));
+      FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
+                          Unretained(watcher.get()), &event, write_fd_task));
 
   // Queue |event| to signal on |loop|.
   loop.task_runner()->PostTask(
-      FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event)));
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
 
   // Now run the MessageLoop.
   run_loop.Run();
@@ -272,7 +272,7 @@
   // StartWatching can move |watcher| to IO thread. Release on IO thread.
   io_loop()->task_runner()->PostTask(
       FROM_HERE,
-      Bind(&WaitableEventWatcher::StopWatching, Owned(watcher.release())));
+      BindOnce(&WaitableEventWatcher::StopWatching, Owned(watcher.release())));
 }
 
 }  // namespace
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
index 04a98c2..e9629aa 100644
--- a/base/message_loop/message_pump_perftest.cc
+++ b/base/message_loop/message_pump_perftest.cc
@@ -65,8 +65,8 @@
     min_batch_times_[index] = minimum;
     max_batch_times_[index] = maximum;
     target_message_loop()->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&ScheduleWorkTest::Increment,
-                              base::Unretained(this), schedule_calls));
+        FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
+                                  base::Unretained(this), schedule_calls));
   }
 
   void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
@@ -101,8 +101,8 @@
 
     for (int i = 0; i < num_scheduling_threads; ++i) {
       scheduling_threads[i]->task_runner()->PostTask(
-          FROM_HERE,
-          base::Bind(&ScheduleWorkTest::Schedule, base::Unretained(this), i));
+          FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
+                                    base::Unretained(this), i));
     }
 
     for (int i = 0; i < num_scheduling_threads; ++i) {
@@ -263,8 +263,8 @@
     do {
       for (int i = 0; i < batch_size; ++i) {
         for (int j = 0; j < tasks_per_reload; ++j) {
-          queue->AddToIncomingQueue(
-              FROM_HERE, base::Bind(&DoNothing), base::TimeDelta(), false);
+          queue->AddToIncomingQueue(FROM_HERE, base::BindOnce(&DoNothing),
+                                    base::TimeDelta(), false);
           num_posted++;
         }
         TaskQueue loop_local_queue;
diff --git a/base/metrics/user_metrics.cc b/base/metrics/user_metrics.cc
index 65ac918..9fcc9e8a 100644
--- a/base/metrics/user_metrics.cc
+++ b/base/metrics/user_metrics.cc
@@ -36,7 +36,7 @@
 
   if (!g_task_runner.Get()->BelongsToCurrentThread()) {
     g_task_runner.Get()->PostTask(FROM_HERE,
-                                  Bind(&RecordComputedAction, action));
+                                  BindOnce(&RecordComputedAction, action));
     return;
   }
 
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h
index 9e6e347..c175c178 100644
--- a/base/observer_list_threadsafe.h
+++ b/base/observer_list_threadsafe.h
@@ -142,8 +142,8 @@
     for (const auto& observer : observers_) {
       observer.second->PostTask(
           from_here,
-          Bind(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this,
-               observer.first, NotificationData(from_here, method)));
+          BindOnce(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this,
+                   observer.first, NotificationData(from_here, method)));
     }
   }
 
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc
index cc7889f..d0248c9 100644
--- a/base/observer_list_unittest.cc
+++ b/base/observer_list_unittest.cc
@@ -118,7 +118,7 @@
     loop_ = new MessageLoop();  // Fire up a message loop.
     loop_->task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
+        base::BindOnce(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
     RunLoop().Run();
     delete loop_;
     loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
@@ -146,7 +146,7 @@
 
     loop_->task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
+        base::BindOnce(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
   }
 
   void Quit() {
diff --git a/base/posix/unix_domain_socket_linux_unittest.cc b/base/posix/unix_domain_socket_linux_unittest.cc
index 3f5173cf..47ba622 100644
--- a/base/posix/unix_domain_socket_linux_unittest.cc
+++ b/base/posix/unix_domain_socket_linux_unittest.cc
@@ -36,9 +36,9 @@
   // Have the thread send a synchronous message via the socket.
   Pickle request;
   message_thread.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg), fds[1],
-           static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL), request));
+      FROM_HERE, BindOnce(IgnoreResult(&UnixDomainSocket::SendRecvMsg), fds[1],
+                          static_cast<uint8_t*>(NULL), 0U,
+                          static_cast<int*>(NULL), request));
 
   // Receive the message.
   std::vector<ScopedFD> message_fds;
@@ -55,7 +55,7 @@
   WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
                       WaitableEvent::InitialState::NOT_SIGNALED);
   message_thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event)));
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
   ASSERT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(5000)));
 }
 
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index a5754cf..ed98020 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -377,9 +377,9 @@
   std::vector<std::string> vec2;
   std::vector<std::string> vec3;
 
-  thread1.task_runner()->PostTask(FROM_HERE, Bind(&BusyWork, &vec1));
-  thread2.task_runner()->PostTask(FROM_HERE, Bind(&BusyWork, &vec2));
-  thread3.task_runner()->PostTask(FROM_HERE, Bind(&BusyWork, &vec3));
+  thread1.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec1));
+  thread2.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec2));
+  thread3.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec3));
 
   EXPECT_GE(metrics->GetCPUUsage(), 0.0);
 
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index a87ced0..5be97d4 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -35,11 +35,11 @@
 
   // This task should quit |nested_run_loop| but not the main RunLoop.
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&QuitWhenIdleTask, Unretained(&nested_run_loop),
-                      Unretained(counter)));
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&nested_run_loop),
+                          Unretained(counter)));
 
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1));
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
 
   MessageLoop::ScopedNestableTaskAllower allower(MessageLoop::current());
   nested_run_loop.Run();
@@ -63,12 +63,12 @@
 
 TEST_F(RunLoopTest, QuitWhenIdle) {
   message_loop_.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(&QuitWhenIdleTask, Unretained(&run_loop_), Unretained(&counter_)));
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_),
+                          Unretained(&counter_)));
   message_loop_.task_runner()->PostTask(
-      FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_)));
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
   message_loop_.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1));
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
 
   run_loop_.Run();
   EXPECT_EQ(2, counter_);
@@ -76,14 +76,14 @@
 
 TEST_F(RunLoopTest, QuitWhenIdleNestedLoop) {
   message_loop_.task_runner()->PostTask(
-      FROM_HERE, Bind(&RunNestedLoopTask, Unretained(&counter_)));
+      FROM_HERE, BindOnce(&RunNestedLoopTask, Unretained(&counter_)));
   message_loop_.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(&QuitWhenIdleTask, Unretained(&run_loop_), Unretained(&counter_)));
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_),
+                          Unretained(&counter_)));
   message_loop_.task_runner()->PostTask(
-      FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_)));
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
   message_loop_.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1));
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
 
   run_loop_.Run();
   EXPECT_EQ(4, counter_);
@@ -93,9 +93,9 @@
   message_loop_.task_runner()->PostTask(FROM_HERE,
                                         run_loop_.QuitWhenIdleClosure());
   message_loop_.task_runner()->PostTask(
-      FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_)));
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
   message_loop_.task_runner()->PostDelayedTask(
-      FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1));
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
 
   run_loop_.Run();
   EXPECT_EQ(1, counter_);
diff --git a/base/sequence_checker_unittest.cc b/base/sequence_checker_unittest.cc
index 86e9298..41fd77be6 100644
--- a/base/sequence_checker_unittest.cc
+++ b/base/sequence_checker_unittest.cc
@@ -250,8 +250,9 @@
 
   SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2");
   second_pool_owner.pool()->PostNamedSequencedWorkerTask(
-      "A", FROM_HERE, base::Bind(&ExpectNotCalledOnValidSequence,
-                                 base::Unretained(&sequence_checker)));
+      "A", FROM_HERE,
+      base::BindOnce(&ExpectNotCalledOnValidSequence,
+                     base::Unretained(&sequence_checker)));
   second_pool_owner.pool()->FlushForTesting();
 }
 
diff --git a/base/sequenced_task_runner.cc b/base/sequenced_task_runner.cc
index 4c367cb..ff859c8 100644
--- a/base/sequenced_task_runner.cc
+++ b/base/sequenced_task_runner.cc
@@ -21,7 +21,7 @@
     const tracked_objects::Location& from_here,
     void (*deleter)(const void*),
     const void* object) {
-  return PostNonNestableTask(from_here, Bind(deleter, object));
+  return PostNonNestableTask(from_here, BindOnce(deleter, object));
 }
 
 OnTaskRunnerDeleter::OnTaskRunnerDeleter(
diff --git a/base/sequenced_task_runner_unittest.cc b/base/sequenced_task_runner_unittest.cc
index b999ffc8..93ac20f 100644
--- a/base/sequenced_task_runner_unittest.cc
+++ b/base/sequenced_task_runner_unittest.cc
@@ -47,16 +47,12 @@
                              OnTaskRunnerDeleter(current_thread));
   EXPECT_EQ(0, counter);
   foreign_thread->PostTask(
-      FROM_HERE,
-      Bind([](SequenceBoundUniquePtr) {},
-           Passed(&ptr)));
+      FROM_HERE, BindOnce([](SequenceBoundUniquePtr) {}, Passed(&ptr)));
 
   {
     RunLoop run_loop;
-    foreign_thread->PostTaskAndReply(
-        FROM_HERE,
-        Bind([]{}),
-        run_loop.QuitClosure());
+    foreign_thread->PostTaskAndReply(FROM_HERE, BindOnce([] {}),
+                                     run_loop.QuitClosure());
     run_loop.Run();
   }
   EXPECT_EQ(1, counter);
diff --git a/base/synchronization/atomic_flag_unittest.cc b/base/synchronization/atomic_flag_unittest.cc
index a3aa3341..76e5d968 100644
--- a/base/synchronization/atomic_flag_unittest.cc
+++ b/base/synchronization/atomic_flag_unittest.cc
@@ -68,10 +68,9 @@
 
   Thread thread("AtomicFlagTest.ReadFromDifferentThread");
   ASSERT_TRUE(thread.Start());
-  thread.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(&BusyWaitUntilFlagIsSet, &tested_flag, &expected_after_flag,
-           &reset_flag));
+  thread.task_runner()->PostTask(FROM_HERE,
+                                 BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag,
+                                          &expected_after_flag, &reset_flag));
 
   // To verify that IsSet() fetches the flag's value from memory every time it
   // is called (not just the first time that it is called on a thread), sleep
@@ -100,10 +99,9 @@
   // |thread| is guaranteed to be synchronized past the
   // |UnsafeResetForTesting()| call when the task runs per the implicit
   // synchronization in the post task mechanism.
-  thread.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(&BusyWaitUntilFlagIsSet, &tested_flag, &expected_after_flag,
-           nullptr));
+  thread.task_runner()->PostTask(FROM_HERE,
+                                 BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag,
+                                          &expected_after_flag, nullptr));
 
   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
 
@@ -125,7 +123,7 @@
 
   AtomicFlag flag;
   flag.Set();
-  t.task_runner()->PostTask(FROM_HERE, Bind(&ExpectSetFlagDeath, &flag));
+  t.task_runner()->PostTask(FROM_HERE, BindOnce(&ExpectSetFlagDeath, &flag));
 }
 
 }  // namespace base
diff --git a/base/synchronization/condition_variable_unittest.cc b/base/synchronization/condition_variable_unittest.cc
index d60b2b8..ebdbe577 100644
--- a/base/synchronization/condition_variable_unittest.cc
+++ b/base/synchronization/condition_variable_unittest.cc
@@ -225,7 +225,7 @@
 
   Thread thread("Helper");
   thread.Start();
-  thread.task_runner()->PostTask(FROM_HERE, base::Bind(&BackInTime, &lock));
+  thread.task_runner()->PostTask(FROM_HERE, base::BindOnce(&BackInTime, &lock));
 
   TimeTicks start = TimeTicks::Now();
   const TimeDelta kWaitTime = TimeDelta::FromMilliseconds(300);
diff --git a/base/task/cancelable_task_tracker.cc b/base/task/cancelable_task_tracker.cc
index 2a68a57b..e68b9591 100644
--- a/base/task/cancelable_task_tracker.cc
+++ b/base/task/cancelable_task_tracker.cc
@@ -90,9 +90,9 @@
   Closure untrack_closure =
       Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id);
   bool success = task_runner->PostTaskAndReply(
-      from_here, Bind(&RunIfNotCanceled, flag, std::move(task)),
-      Bind(&RunIfNotCanceledThenUntrack, base::Owned(flag), std::move(reply),
-           std::move(untrack_closure)));
+      from_here, BindOnce(&RunIfNotCanceled, flag, std::move(task)),
+      BindOnce(&RunIfNotCanceledThenUntrack, base::Owned(flag),
+               std::move(reply), std::move(untrack_closure)));
 
   if (!success)
     return kBadTaskId;
diff --git a/base/task/cancelable_task_tracker_unittest.cc b/base/task/cancelable_task_tracker_unittest.cc
index fd480f36..a16f5af95 100644
--- a/base/task/cancelable_task_tracker_unittest.cc
+++ b/base/task/cancelable_task_tracker_unittest.cc
@@ -194,14 +194,14 @@
   Thread other_thread("other thread");
   ASSERT_TRUE(other_thread.Start());
   other_thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, false));
+      FROM_HERE, BindOnce(&ExpectIsCanceled, is_canceled, false));
   other_thread.Stop();
 
   task_tracker_.TryCancel(task_id);
 
   ASSERT_TRUE(other_thread.Start());
   other_thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true));
+      FROM_HERE, BindOnce(&ExpectIsCanceled, is_canceled, true));
   other_thread.Stop();
 }
 
@@ -364,8 +364,9 @@
   ASSERT_TRUE(bad_thread.Start());
 
   bad_thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
-                      Unretained(&task_tracker_), Bind(&PostDoNothingTask)));
+      FROM_HERE,
+      BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
+               Unretained(&task_tracker_), Bind(&PostDoNothingTask)));
 }
 
 void TryCancel(CancelableTaskTracker::TaskId task_id,
@@ -385,8 +386,9 @@
   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
 
   bad_thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
-                      Unretained(&task_tracker_), Bind(&TryCancel, task_id)));
+      FROM_HERE,
+      BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
+               Unretained(&task_tracker_), Bind(&TryCancel, task_id)));
 
   test_task_runner->RunUntilIdle();
 }
@@ -403,9 +405,9 @@
   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
 
   bad_thread.task_runner()->PostTask(
-      FROM_HERE,
-      Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, Unretained(&task_tracker_),
-           Bind(&CancelableTaskTracker::TryCancelAll)));
+      FROM_HERE, BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
+                          Unretained(&task_tracker_),
+                          Bind(&CancelableTaskTracker::TryCancelAll)));
 
   test_task_runner->RunUntilIdle();
 }
diff --git a/base/task_runner_util.h b/base/task_runner_util.h
index 7fda076..9196bf1 100644
--- a/base/task_runner_util.h
+++ b/base/task_runner_util.h
@@ -39,10 +39,11 @@
   DCHECK(reply);
   TaskReturnType* result = new TaskReturnType();
   return task_runner->PostTaskAndReply(
-      from_here, base::Bind(&internal::ReturnAsParamAdapter<TaskReturnType>,
-                            std::move(task), result),
-      base::Bind(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
-                 std::move(reply), base::Owned(result)));
+      from_here,
+      base::BindOnce(&internal::ReturnAsParamAdapter<TaskReturnType>,
+                     std::move(task), result),
+      base::BindOnce(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
+                     std::move(reply), base::Owned(result)));
 }
 
 }  // namespace base
diff --git a/base/task_scheduler/delayed_task_manager.cc b/base/task_scheduler/delayed_task_manager.cc
index 420f893..570ffcc 100644
--- a/base/task_scheduler/delayed_task_manager.cc
+++ b/base/task_scheduler/delayed_task_manager.cc
@@ -34,7 +34,8 @@
   // TODO(fdoray): Use |task->delayed_run_time| on the service thread
   // MessageLoop rather than recomputing it from |delay|.
   service_thread_task_runner_->PostDelayedTask(
-      FROM_HERE, Bind(post_task_now_callback, Passed(std::move(task))), delay);
+      FROM_HERE, BindOnce(post_task_now_callback, Passed(std::move(task))),
+      delay);
 }
 
 }  // namespace internal
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
index 0c3c0b6..8244bb5 100644
--- a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
+++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
@@ -124,9 +124,11 @@
                   TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
   PlatformThreadRef thread_ref_1;
-  task_runner_1->PostTask(FROM_HERE, Bind(&CaptureThreadRef, &thread_ref_1));
+  task_runner_1->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_1));
   PlatformThreadRef thread_ref_2;
-  task_runner_2->PostTask(FROM_HERE, Bind(&CaptureThreadRef, &thread_ref_2));
+  task_runner_2->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_2));
 
   task_tracker_.Shutdown();
 
@@ -156,33 +158,35 @@
 
   ThreadPriority thread_priority_background;
   task_runner_background->PostTask(
-      FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_background));
+      FROM_HERE, BindOnce(&CaptureThreadPriority, &thread_priority_background));
   WaitableEvent waitable_event_background(
       WaitableEvent::ResetPolicy::MANUAL,
       WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner_background->PostTask(
       FROM_HERE,
-      Bind(&WaitableEvent::Signal, Unretained(&waitable_event_background)));
+      BindOnce(&WaitableEvent::Signal, Unretained(&waitable_event_background)));
 
   ThreadPriority thread_priority_user_visible;
   task_runner_user_visible->PostTask(
-      FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_user_visible));
+      FROM_HERE,
+      BindOnce(&CaptureThreadPriority, &thread_priority_user_visible));
   WaitableEvent waitable_event_user_visible(
       WaitableEvent::ResetPolicy::MANUAL,
       WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner_user_visible->PostTask(
-      FROM_HERE,
-      Bind(&WaitableEvent::Signal, Unretained(&waitable_event_user_visible)));
+      FROM_HERE, BindOnce(&WaitableEvent::Signal,
+                          Unretained(&waitable_event_user_visible)));
 
   ThreadPriority thread_priority_user_blocking;
   task_runner_user_blocking->PostTask(
-      FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_user_blocking));
+      FROM_HERE,
+      BindOnce(&CaptureThreadPriority, &thread_priority_user_blocking));
   WaitableEvent waitable_event_user_blocking(
       WaitableEvent::ResetPolicy::MANUAL,
       WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner_user_blocking->PostTask(
-      FROM_HERE,
-      Bind(&WaitableEvent::Signal, Unretained(&waitable_event_user_blocking)));
+      FROM_HERE, BindOnce(&WaitableEvent::Signal,
+                          Unretained(&waitable_event_user_blocking)));
 
   waitable_event_background.Wait();
   waitable_event_user_visible.Wait();
@@ -200,7 +204,7 @@
   auto task_runner = single_thread_task_runner_manager_
                          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits());
   task_tracker_.Shutdown();
-  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, Bind(&ShouldNotRun)));
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
 // Verify that a Task runs shortly after its delay expires.
@@ -213,7 +217,7 @@
   auto task_runner = single_thread_task_runner_manager_
                          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits());
   EXPECT_TRUE(task_runner->PostDelayedTask(
-      FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_ran)),
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)),
       TestTimeouts::tiny_timeout()));
 
   // Wait until the task runs.
@@ -244,7 +248,7 @@
   EXPECT_FALSE(task_runner_2->RunsTasksOnCurrentThread());
 
   task_runner_1->PostTask(
-      FROM_HERE, Bind(
+      FROM_HERE, BindOnce(
                      [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
                         scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
                        EXPECT_TRUE(task_runner_1->RunsTasksOnCurrentThread());
@@ -253,7 +257,7 @@
                      task_runner_1, task_runner_2));
 
   task_runner_2->PostTask(
-      FROM_HERE, Bind(
+      FROM_HERE, BindOnce(
                      [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
                         scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
                        EXPECT_FALSE(task_runner_1->RunsTasksOnCurrentThread());
@@ -322,9 +326,10 @@
                            ->CreateSingleThreadTaskRunnerWithTraits(
                                TaskTraits().WithBaseSyncPrimitives());
     EXPECT_TRUE(task_runner->PostTask(
-        FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_running))));
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Signal, Unretained(&task_running))));
     EXPECT_TRUE(task_runner->PostTask(
-        FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&task_blocking))));
+        FROM_HERE, BindOnce(&WaitableEvent::Wait, Unretained(&task_blocking))));
   }
 
   task_running.Wait();
@@ -350,10 +355,11 @@
                            ->CreateSingleThreadTaskRunnerWithTraits(
                                TaskTraits().WithBaseSyncPrimitives());
     EXPECT_TRUE(task_runner->PostTask(
-        FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_running))));
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Signal, Unretained(&task_running))));
     EXPECT_TRUE(task_runner->PostTask(
-        FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&task_blocking))));
-    EXPECT_TRUE(task_runner->PostTask(FROM_HERE, Bind(&DoNothing)));
+        FROM_HERE, BindOnce(&WaitableEvent::Wait, Unretained(&task_blocking))));
+    EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing)));
   }
 
   task_running.Wait();
diff --git a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
index 86f63272..ea5cd51 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
@@ -327,7 +327,7 @@
   auto task_runner =
       CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam());
   task_tracker_.Shutdown();
-  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, Bind(&ShouldNotRun)));
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
 // Verify that a Task runs shortly after its delay expires.
@@ -338,9 +338,10 @@
   WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
                          WaitableEvent::InitialState::NOT_SIGNALED);
   EXPECT_TRUE(CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam())
-                  ->PostDelayedTask(FROM_HERE, Bind(&WaitableEvent::Signal,
-                                                    Unretained(&task_ran)),
-                                    TestTimeouts::tiny_timeout()));
+                  ->PostDelayedTask(
+                      FROM_HERE,
+                      BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)),
+                      TestTimeouts::tiny_timeout()));
 
   // Wait until the task runs.
   task_ran.Wait();
@@ -368,7 +369,7 @@
                          WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](scoped_refptr<TaskRunner> sequenced_task_runner,
              WaitableEvent* task_ran) {
             EXPECT_FALSE(sequenced_task_runner->RunsTasksOnCurrentThread());
@@ -579,13 +580,13 @@
 
   // Post a task.
   task_runner->PostTask(FROM_HERE,
-                        Bind(&WaitableEvent::Wait, Unretained(&event)));
+                        BindOnce(&WaitableEvent::Wait, Unretained(&event)));
 
   // Post 2 more tasks while the first task hasn't completed its execution. It
   // is guaranteed that these tasks will run immediately after the first task,
   // without allowing the worker to sleep.
-  task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
-  task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
+  task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing));
+  task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing));
 
   // Allow tasks to run and wait until the SchedulerWorker is idle.
   event.Signal();
@@ -594,7 +595,7 @@
   // Wake up the SchedulerWorker that just became idle by posting a task and
   // wait until it becomes idle again. The SchedulerWorker should record the
   // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
-  task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
+  task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing));
   worker_pool_->WaitForAllWorkersIdleForTesting();
 
   // Verify that counts were recorded to the histogram as expected.
@@ -627,10 +628,10 @@
     task_started_events.push_back(
         MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
                                   WaitableEvent::InitialState::NOT_SIGNALED));
-    task_runner->PostTask(
-        FROM_HERE,
-        Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
-             Unretained(&tasks_can_exit_event)));
+    task_runner->PostTask(FROM_HERE,
+                          BindOnce(&SignalAndWaitEvent,
+                                   Unretained(task_started_events.back().get()),
+                                   Unretained(&tasks_can_exit_event)));
   }
   for (const auto& task_started_event : task_started_events)
     task_started_event->Wait();
@@ -649,10 +650,10 @@
     task_started_events.push_back(
         MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
                                   WaitableEvent::InitialState::NOT_SIGNALED));
-    task_runner->PostTask(
-        FROM_HERE,
-        Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
-             Unretained(&tasks_can_exit_event)));
+    task_runner->PostTask(FROM_HERE,
+                          BindOnce(&SignalAndWaitEvent,
+                                   Unretained(task_started_events.back().get()),
+                                   Unretained(&tasks_can_exit_event)));
   }
   for (const auto& task_started_event : task_started_events)
     task_started_event->Wait();
@@ -690,14 +691,14 @@
   // thread for each of its tasks.
   PlatformThreadRef thread_ref;
   histogrammed_thread_task_runner->PostTask(
-      FROM_HERE, Bind(
+      FROM_HERE, BindOnce(
                      [](PlatformThreadRef* thread_ref) {
                        ASSERT_TRUE(thread_ref);
                        *thread_ref = PlatformThread::CurrentRef();
                      },
                      Unretained(&thread_ref)));
   histogrammed_thread_task_runner->PostTask(
-      FROM_HERE, Bind(
+      FROM_HERE, BindOnce(
                      [](PlatformThreadRef* thread_ref) {
                        ASSERT_FALSE(thread_ref->is_null());
                        EXPECT_EQ(*thread_ref, PlatformThread::CurrentRef());
@@ -712,7 +713,7 @@
       WaitableEvent::InitialState::NOT_SIGNALED);
   histogrammed_thread_task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](PlatformThreadRef* thread_ref,
              WaitableEvent* detach_thread_running,
              WaitableEvent* detach_thread_continue) {
@@ -743,7 +744,7 @@
       worker_pool_->CreateSequencedTaskRunnerWithTraits(
           TaskTraits().WithBaseSyncPrimitives());
   task_runner_for_top_idle->PostTask(
-      FROM_HERE, Bind(
+      FROM_HERE, BindOnce(
                      [](PlatformThreadRef thread_ref,
                         WaitableEvent* top_idle_thread_running,
                         WaitableEvent* top_idle_thread_continue) {
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc b/base/task_scheduler/scheduler_worker_unittest.cc
index b8dea8e..747c80b 100644
--- a/base/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_unittest.cc
@@ -172,10 +172,11 @@
       // Create a Sequence with TasksPerSequence() Tasks.
       scoped_refptr<Sequence> sequence(new Sequence);
       for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) {
-        std::unique_ptr<Task> task(new Task(
-            FROM_HERE, Bind(&TaskSchedulerWorkerTest::RunTaskCallback,
-                            Unretained(outer_)),
-            TaskTraits(), TimeDelta()));
+        std::unique_ptr<Task> task(
+            new Task(FROM_HERE,
+                     BindOnce(&TaskSchedulerWorkerTest::RunTaskCallback,
+                              Unretained(outer_)),
+                     TaskTraits(), TimeDelta()));
         EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get()));
         sequence->PushTask(std::move(task));
       }
@@ -434,7 +435,7 @@
     scoped_refptr<Sequence> sequence(new Sequence);
     std::unique_ptr<Task> task(new Task(
         FROM_HERE,
-        Bind(
+        BindOnce(
             [](WaitableEvent* work_processed, WaitableEvent* work_running) {
               work_processed->Signal();
               work_running->Wait();
diff --git a/base/task_scheduler/task_scheduler_impl_unittest.cc b/base/task_scheduler/task_scheduler_impl_unittest.cc
index 1489bae..e0f8869 100644
--- a/base/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task_scheduler/task_scheduler_impl_unittest.cc
@@ -242,8 +242,8 @@
                          WaitableEvent::InitialState::NOT_SIGNALED);
   scheduler_->PostDelayedTaskWithTraits(
       FROM_HERE, GetParam().traits,
-      Bind(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
-           Unretained(&task_ran)),
+      BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
+               Unretained(&task_ran)),
       TimeDelta());
   task_ran.Wait();
 }
@@ -257,9 +257,9 @@
                          WaitableEvent::InitialState::NOT_SIGNALED);
   scheduler_->PostDelayedTaskWithTraits(
       FROM_HERE, GetParam().traits,
-      Bind(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
-           TimeTicks::Now() + TestTimeouts::tiny_timeout(),
-           Unretained(&task_ran)),
+      BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
+               TimeTicks::Now() + TestTimeouts::tiny_timeout(),
+               Unretained(&task_ran)),
       TestTimeouts::tiny_timeout());
   task_ran.Wait();
 }
@@ -338,7 +338,7 @@
                          WaitableEvent::InitialState::NOT_SIGNALED);
   single_thread_task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](scoped_refptr<TaskRunner> sequenced_task_runner,
              WaitableEvent* task_ran) {
             EXPECT_FALSE(sequenced_task_runner->RunsTasksOnCurrentThread());
@@ -360,7 +360,7 @@
                          WaitableEvent::InitialState::NOT_SIGNALED);
   sequenced_task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](scoped_refptr<TaskRunner> single_thread_task_runner,
              WaitableEvent* task_ran) {
             EXPECT_FALSE(single_thread_task_runner->RunsTasksOnCurrentThread());
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
index 0005e6c..1c9b0a8 100644
--- a/base/task_scheduler/task_tracker_unittest.cc
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -428,7 +428,7 @@
 
   TaskTracker tracker;
   std::unique_ptr<Task> task(
-      new Task(FROM_HERE, Bind(&ThreadRestrictions::AssertSingletonAllowed),
+      new Task(FROM_HERE, BindOnce(&ThreadRestrictions::AssertSingletonAllowed),
                TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
   EXPECT_TRUE(tracker.WillPostTask(task.get()));
 
@@ -502,7 +502,7 @@
   // Create a task that will verify that TaskRunnerHandles are not set in its
   // scope per no TaskRunner ref being set to it.
   std::unique_ptr<Task> verify_task(
-      new Task(FROM_HERE, Bind(&VerifyNoTaskRunnerHandle),
+      new Task(FROM_HERE, BindOnce(&VerifyNoTaskRunnerHandle),
                TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
 
   RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
@@ -523,8 +523,9 @@
   // set to |test_task_runner| in its scope per |sequenced_task_runner_ref|
   // being set to it.
   std::unique_ptr<Task> verify_task(
-      new Task(FROM_HERE, Bind(&VerifySequencedTaskRunnerHandle,
-                               base::Unretained(test_task_runner.get())),
+      new Task(FROM_HERE,
+               BindOnce(&VerifySequencedTaskRunnerHandle,
+                        base::Unretained(test_task_runner.get())),
                TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
   verify_task->sequenced_task_runner_ref = test_task_runner;
 
@@ -548,8 +549,9 @@
   // to |test_task_runner| in its scope per |single_thread_task_runner_ref|
   // being set on it.
   std::unique_ptr<Task> verify_task(
-      new Task(FROM_HERE, Bind(&VerifyThreadTaskRunnerHandle,
-                               base::Unretained(test_task_runner.get())),
+      new Task(FROM_HERE,
+               BindOnce(&VerifyThreadTaskRunnerHandle,
+                        base::Unretained(test_task_runner.get())),
                TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
   verify_task->single_thread_task_runner_ref = test_task_runner;
 
@@ -557,7 +559,7 @@
 }
 
 TEST_P(TaskSchedulerTaskTrackerTest, FlushPendingDelayedTask) {
-  const Task delayed_task(FROM_HERE, Bind(&DoNothing),
+  const Task delayed_task(FROM_HERE, BindOnce(&DoNothing),
                           TaskTraits().WithShutdownBehavior(GetParam()),
                           TimeDelta::FromDays(1));
   tracker_.WillPostTask(&delayed_task);
diff --git a/base/task_scheduler/task_unittest.cc b/base/task_scheduler/task_unittest.cc
index e6a2c51..fb076d76 100644
--- a/base/task_scheduler/task_unittest.cc
+++ b/base/task_scheduler/task_unittest.cc
@@ -17,7 +17,7 @@
 // adjusted to SKIP_ON_SHUTDOWN. The shutown behavior of other delayed tasks
 // should not change.
 TEST(TaskSchedulerTaskTest, ShutdownBehaviorChangeWithDelay) {
-  Task continue_on_shutdown(FROM_HERE, Bind(&DoNothing),
+  Task continue_on_shutdown(FROM_HERE, BindOnce(&DoNothing),
                             TaskTraits().WithShutdownBehavior(
                                 TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
                             TimeDelta::FromSeconds(1));
@@ -25,14 +25,14 @@
             continue_on_shutdown.traits.shutdown_behavior());
 
   Task skip_on_shutdown(
-      FROM_HERE, Bind(&DoNothing),
+      FROM_HERE, BindOnce(&DoNothing),
       TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
       TimeDelta::FromSeconds(1));
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
             skip_on_shutdown.traits.shutdown_behavior());
 
   Task block_shutdown(
-      FROM_HERE, Bind(&DoNothing),
+      FROM_HERE, BindOnce(&DoNothing),
       TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::BLOCK_SHUTDOWN),
       TimeDelta::FromSeconds(1));
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
@@ -41,7 +41,7 @@
 
 // Verify that the shutdown behavior of undelayed tasks is not adjusted.
 TEST(TaskSchedulerTaskTest, NoShutdownBehaviorChangeNoDelay) {
-  Task continue_on_shutdown(FROM_HERE, Bind(&DoNothing),
+  Task continue_on_shutdown(FROM_HERE, BindOnce(&DoNothing),
                             TaskTraits().WithShutdownBehavior(
                                 TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
                             TimeDelta());
@@ -49,14 +49,14 @@
             continue_on_shutdown.traits.shutdown_behavior());
 
   Task skip_on_shutdown(
-      FROM_HERE, Bind(&DoNothing),
+      FROM_HERE, BindOnce(&DoNothing),
       TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
       TimeDelta());
   EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
             skip_on_shutdown.traits.shutdown_behavior());
 
   Task block_shutdown(
-      FROM_HERE, Bind(&DoNothing),
+      FROM_HERE, BindOnce(&DoNothing),
       TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::BLOCK_SHUTDOWN),
       TimeDelta());
   EXPECT_EQ(TaskShutdownBehavior::BLOCK_SHUTDOWN,
diff --git a/base/task_scheduler/test_task_factory.cc b/base/task_scheduler/test_task_factory.cc
index 3b4e723..8db2042 100644
--- a/base/task_scheduler/test_task_factory.cc
+++ b/base/task_scheduler/test_task_factory.cc
@@ -37,8 +37,8 @@
   AutoLock auto_lock(lock_);
   return task_runner_->PostTask(
       FROM_HERE,
-      Bind(&TestTaskFactory::RunTaskCallback, Unretained(this),
-           num_posted_tasks_++, post_nested_task, after_task_closure));
+      BindOnce(&TestTaskFactory::RunTaskCallback, Unretained(this),
+               num_posted_tasks_++, post_nested_task, after_task_closure));
 }
 
 void TestTaskFactory::WaitForAllTasksToRun() const {
diff --git a/base/test/gtest_util.cc b/base/test/gtest_util.cc
index 6da902d..1552c1a 100644
--- a/base/test/gtest_util.cc
+++ b/base/test/gtest_util.cc
@@ -85,7 +85,7 @@
   std::vector<base::TestIdentifier> result;
   for (base::ListValue::iterator i = tests->begin(); i != tests->end(); ++i) {
     base::DictionaryValue* test = nullptr;
-    if (!(*i)->GetAsDictionary(&test))
+    if (!i->GetAsDictionary(&test))
       return false;
 
     TestIdentifier test_data;
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index eb9e369..4553002 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -472,10 +472,10 @@
 
   // Run target callback on the thread it was originating from, not on
   // a worker pool thread.
-  task_runner->PostTask(
-      FROM_HERE,
-      Bind(&RunCallback, completed_callback, exit_code,
-           TimeTicks::Now() - start_time, was_timeout, output_file_contents));
+  task_runner->PostTask(FROM_HERE,
+                        BindOnce(&RunCallback, completed_callback, exit_code,
+                                 TimeTicks::Now() - start_time, was_timeout,
+                                 output_file_contents));
 }
 
 }  // namespace
@@ -544,7 +544,7 @@
   watchdog_timer_.Reset();
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+      FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
 
   RunLoop().Run();
 
@@ -576,11 +576,11 @@
 
   GetTaskRunner()->PostTask(
       FROM_HERE,
-      Bind(&DoLaunchChildTestProcess, new_command_line, timeout, options,
-           redirect_stdio, RetainedRef(ThreadTaskRunnerHandle::Get()),
-           Bind(&TestLauncher::OnLaunchTestProcessFinished, Unretained(this),
-                completed_callback),
-           launched_callback));
+      BindOnce(&DoLaunchChildTestProcess, new_command_line, timeout, options,
+               redirect_stdio, RetainedRef(ThreadTaskRunnerHandle::Get()),
+               Bind(&TestLauncher::OnLaunchTestProcessFinished,
+                    Unretained(this), completed_callback),
+               launched_callback));
 }
 
 void TestLauncher::OnTestFinished(const TestResult& original_result) {
@@ -1071,7 +1071,7 @@
 
     // No tests have actually been started, so kick off the next iteration.
     ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+        FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
   }
 }
 
@@ -1097,7 +1097,7 @@
   results_tracker_.OnTestIterationStarting();
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&TestLauncher::RunTests, Unretained(this)));
+      FROM_HERE, BindOnce(&TestLauncher::RunTests, Unretained(this)));
 }
 
 void TestLauncher::MaybeSaveSummaryAsJSON(
@@ -1152,7 +1152,7 @@
 
   // Kick off the next iteration.
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+      FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
 }
 
 void TestLauncher::OnOutputTimeout() {
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index c9f0eb0a..44c440f 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -439,9 +439,9 @@
   DeleteFile(callback_state.output_file.DirName(), true);
 
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, Bind(&RunUnitTestsSerially, callback_state.test_launcher,
-                      callback_state.platform_delegate, test_names,
-                      callback_state.launch_flags));
+      FROM_HERE, BindOnce(&RunUnitTestsSerially, callback_state.test_launcher,
+                          callback_state.platform_delegate, test_names,
+                          callback_state.launch_flags));
 }
 
 }  // namespace
@@ -636,8 +636,9 @@
     const std::vector<std::string>& test_names) {
   ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      Bind(&RunUnitTestsSerially, test_launcher, platform_delegate_, test_names,
-           use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0));
+      BindOnce(&RunUnitTestsSerially, test_launcher, platform_delegate_,
+               test_names,
+               use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0));
   return test_names.size();
 }
 
diff --git a/base/test/scoped_mock_time_message_loop_task_runner.cc b/base/test/scoped_mock_time_message_loop_task_runner.cc
index 5b1c03f..15b07fc 100644
--- a/base/test/scoped_mock_time_message_loop_task_runner.cc
+++ b/base/test/scoped_mock_time_message_loop_task_runner.cc
@@ -41,7 +41,7 @@
     // to OnceClosure.
     previous_task_runner_->PostDelayedTask(
         pending_task.location,
-        Bind(&RunOnceClosure, Passed(&pending_task.task)),
+        BindOnce(&RunOnceClosure, Passed(&pending_task.task)),
         pending_task.GetTimeToRun() - task_runner_->NowTicks());
   }
   MessageLoop::current()->SetTaskRunner(std::move(previous_task_runner_));
diff --git a/base/test/scoped_task_scheduler.cc b/base/test/scoped_task_scheduler.cc
index 4020e0e..3c5b0e2f 100644
--- a/base/test/scoped_task_scheduler.cc
+++ b/base/test/scoped_task_scheduler.cc
@@ -236,8 +236,9 @@
     return false;
   internal::Task* const task_ptr = task.get();
   return MessageLoopTaskRunner()->PostDelayedTask(
-      task_ptr->posted_from, Bind(&TestTaskScheduler::RunTask, Unretained(this),
-                                  Passed(&task), sequence_token),
+      task_ptr->posted_from,
+      BindOnce(&TestTaskScheduler::RunTask, Unretained(this), Passed(&task),
+               sequence_token),
       task_ptr->delay);
 }
 
diff --git a/base/test/scoped_task_scheduler_unittest.cc b/base/test/scoped_task_scheduler_unittest.cc
index 27552bf..3323ebf 100644
--- a/base/test/scoped_task_scheduler_unittest.cc
+++ b/base/test/scoped_task_scheduler_unittest.cc
@@ -44,7 +44,7 @@
   thread_checker.DetachFromThread();
 
   PostTask(FROM_HERE,
-           Bind(
+           BindOnce(
                [](SequenceCheckerImpl* sequence_checker,
                   ThreadCheckerImpl* thread_checker, bool* first_task_ran) {
                  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
@@ -57,7 +57,7 @@
                Unretained(&first_task_ran)));
 
   PostTask(FROM_HERE,
-           Bind(
+           BindOnce(
                [](SequenceCheckerImpl* sequence_checker,
                   ThreadCheckerImpl* thread_checker, bool* second_task_ran) {
                  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
@@ -94,7 +94,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* first_task_ran) {
             EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
@@ -108,7 +108,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* second_task_ran) {
             EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
@@ -145,7 +145,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* first_task_ran) {
             EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
@@ -159,7 +159,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* second_task_ran) {
             EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
@@ -196,7 +196,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* first_task_ran) {
             EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
@@ -210,7 +210,7 @@
 
   task_runner->PostTask(
       FROM_HERE,
-      Bind(
+      BindOnce(
           [](SequenceCheckerImpl* sequence_checker,
              ThreadCheckerImpl* thread_checker, bool* second_task_ran) {
             EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
@@ -257,16 +257,18 @@
 TEST(ScopedTaskSchedulerTest, NonBlockShutdownTasksPostedAfterShutdownDontRun) {
   ScopedTaskScheduler scoped_task_scheduler;
   TaskScheduler::GetInstance()->Shutdown();
-  PostTaskWithTraits(FROM_HERE, TaskTraits().WithShutdownBehavior(
-                                    TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
-                     Bind([]() {
+  PostTaskWithTraits(FROM_HERE,
+                     TaskTraits().WithShutdownBehavior(
+                         TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+                     BindOnce([]() {
                        ADD_FAILURE()
                            << "CONTINUE_ON_SHUTDOWN task should not run";
                      }));
   PostTaskWithTraits(
       FROM_HERE,
       TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
-      Bind([]() { ADD_FAILURE() << "SKIP_ON_SHUTDOWN task should not run"; }));
+      BindOnce(
+          []() { ADD_FAILURE() << "SKIP_ON_SHUTDOWN task should not run"; }));
 
   // This should not run anything.
   RunLoop().RunUntilIdle();
@@ -276,25 +278,28 @@
   bool block_shutdown_task_ran = false;
   {
     ScopedTaskScheduler scoped_task_scheduler;
-    PostTaskWithTraits(
-        FROM_HERE, TaskTraits().WithShutdownBehavior(
-                       TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
-        Bind([]() {
-          ADD_FAILURE() << "CONTINUE_ON_SHUTDOWN task should not run";
-        }));
-    PostTaskWithTraits(FROM_HERE, TaskTraits().WithShutdownBehavior(
-                                      TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
-                       Bind([]() {
+    PostTaskWithTraits(FROM_HERE,
+                       TaskTraits().WithShutdownBehavior(
+                           TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+                       BindOnce([]() {
+                         ADD_FAILURE()
+                             << "CONTINUE_ON_SHUTDOWN task should not run";
+                       }));
+    PostTaskWithTraits(FROM_HERE,
+                       TaskTraits().WithShutdownBehavior(
+                           TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
+                       BindOnce([]() {
                          ADD_FAILURE()
                              << "SKIP_ON_SHUTDOWN task should not run";
                        }));
-    PostTaskWithTraits(FROM_HERE, TaskTraits().WithShutdownBehavior(
-                                      TaskShutdownBehavior::BLOCK_SHUTDOWN),
-                       Bind(
-                           [](bool* block_shutdown_task_ran) {
-                             *block_shutdown_task_ran = true;
-                           },
-                           Unretained(&block_shutdown_task_ran)));
+    PostTaskWithTraits(
+        FROM_HERE,
+        TaskTraits().WithShutdownBehavior(TaskShutdownBehavior::BLOCK_SHUTDOWN),
+        BindOnce(
+            [](bool* block_shutdown_task_ran) {
+              *block_shutdown_task_ran = true;
+            },
+            Unretained(&block_shutdown_task_ran)));
   }
   EXPECT_TRUE(block_shutdown_task_ran);
 }
@@ -308,7 +313,8 @@
   ScopedTaskScheduler scoped_task_scheduler;
   {
     ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner;
-    PostDelayedTask(FROM_HERE, Bind(TestTaskRan, Unretained(&first_task_ran)),
+    PostDelayedTask(FROM_HERE,
+                    BindOnce(TestTaskRan, Unretained(&first_task_ran)),
                     TimeDelta::FromSeconds(1));
 
     // The delayed task should be queued on |mock_time_task_runner|, not the
@@ -316,7 +322,8 @@
     EXPECT_TRUE(mock_time_task_runner.task_runner()->HasPendingTask());
   }
 
-  PostDelayedTask(FROM_HERE, Bind(TestTaskRan, Unretained(&second_task_ran)),
+  PostDelayedTask(FROM_HERE,
+                  BindOnce(TestTaskRan, Unretained(&second_task_ran)),
                   TimeDelta());
 
   RunLoop().RunUntilIdle();
@@ -331,14 +338,14 @@
 TEST(ScopedTaskSchedulerTest, ReentrantTaskRunner) {
   bool task_ran = false;
   ScopedTaskScheduler scoped_task_scheduler;
-  PostTask(FROM_HERE, Bind(
-                          [](bool* task_ran) {
-                            PostTask(
-                                FROM_HERE,
-                                Bind([](bool* task_ran) { *task_ran = true; },
-                                     Unretained(task_ran)));
-                          },
-                          Unretained(&task_ran)));
+  PostTask(FROM_HERE,
+           BindOnce(
+               [](bool* task_ran) {
+                 PostTask(FROM_HERE,
+                          BindOnce([](bool* task_ran) { *task_ran = true; },
+                                   Unretained(task_ran)));
+               },
+               Unretained(&task_ran)));
   RunLoop().RunUntilIdle();
   EXPECT_TRUE(task_ran);
 }
diff --git a/base/test/thread_test_helper.cc b/base/test/thread_test_helper.cc
index 97b6647..38edc9d 100644
--- a/base/test/thread_test_helper.cc
+++ b/base/test/thread_test_helper.cc
@@ -21,7 +21,7 @@
 
 bool ThreadTestHelper::Run() {
   if (!target_thread_->PostTask(
-          FROM_HERE, base::Bind(&ThreadTestHelper::RunInThread, this))) {
+          FROM_HERE, base::BindOnce(&ThreadTestHelper::RunInThread, this))) {
     return false;
   }
   base::ThreadRestrictions::ScopedAllowWait allow_wait;
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc
index cddb898..1aaa1e7 100644
--- a/base/threading/post_task_and_reply_impl.cc
+++ b/base/threading/post_task_and_reply_impl.cc
@@ -44,8 +44,8 @@
   void RunTaskAndPostReply() {
     std::move(task_).Run();
     origin_task_runner_->PostTask(
-        from_here_, Bind(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct,
-                         base::Unretained(this)));
+        from_here_, BindOnce(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct,
+                             base::Unretained(this)));
   }
 
  private:
@@ -88,8 +88,8 @@
   // to avoid having to suppress every callsite which happens to flakily trigger
   // this race.
   ANNOTATE_LEAKING_OBJECT_PTR(relay);
-  if (!PostTask(from_here, Bind(&PostTaskAndReplyRelay::RunTaskAndPostReply,
-                                Unretained(relay)))) {
+  if (!PostTask(from_here, BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
+                                    Unretained(relay)))) {
     delete relay;
     return false;
   }
diff --git a/base/threading/post_task_and_reply_impl_unittest.cc b/base/threading/post_task_and_reply_impl_unittest.cc
index f1b1d7a..664c191 100644
--- a/base/threading/post_task_and_reply_impl_unittest.cc
+++ b/base/threading/post_task_and_reply_impl_unittest.cc
@@ -78,9 +78,9 @@
       PostTaskAndReplyTaskRunner(post_runner.get())
           .PostTaskAndReply(
               FROM_HERE,
-              Bind(&MockObject::Task, Unretained(&mock_object),
-                   make_scoped_refptr(new ObjectToDelete(&delete_flag))),
-              Bind(&MockObject::Reply, Unretained(&mock_object))));
+              BindOnce(&MockObject::Task, Unretained(&mock_object),
+                       make_scoped_refptr(new ObjectToDelete(&delete_flag))),
+              BindOnce(&MockObject::Reply, Unretained(&mock_object))));
 
   // Expect the task to be posted to |post_runner|.
   EXPECT_TRUE(post_runner->HasPendingTask());
diff --git a/base/threading/sequenced_task_runner_handle_unittest.cc b/base/threading/sequenced_task_runner_handle_unittest.cc
index 016ab24..6f4948a3 100644
--- a/base/threading/sequenced_task_runner_handle_unittest.cc
+++ b/base/threading/sequenced_task_runner_handle_unittest.cc
@@ -41,8 +41,8 @@
         new SequenceCheckerImpl);
     task_runner->PostTask(
         FROM_HERE,
-        base::Bind(&SequencedTaskRunnerHandleTest::CheckValidSequence,
-                   base::Passed(&sequence_checker), callback));
+        base::BindOnce(&SequencedTaskRunnerHandleTest::CheckValidSequence,
+                       base::Passed(&sequence_checker), callback));
   }
 
   // Verifies that there is no SequencedTaskRunnerHandle in the context it runs.
@@ -77,7 +77,7 @@
                       WaitableEvent::InitialState::NOT_SIGNALED);
   owner.pool()->PostSequencedWorkerTask(
       owner.pool()->GetSequenceToken(), FROM_HERE,
-      base::Bind(
+      base::BindOnce(
           &SequencedTaskRunnerHandleTest::VerifyCurrentSequencedTaskRunner,
           base::Bind(&WaitableEvent::Signal, base::Unretained(&event))));
   event.Wait();
@@ -91,7 +91,7 @@
                       WaitableEvent::InitialState::NOT_SIGNALED);
   owner.pool()->PostWorkerTask(
       FROM_HERE,
-      base::Bind(
+      base::BindOnce(
           &SequencedTaskRunnerHandleTest::VerifyNoSequencedTaskRunner,
           base::Bind(&WaitableEvent::Signal, base::Unretained(&event))));
   event.Wait();
diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc
index 8430d29..d358059 100644
--- a/base/threading/sequenced_worker_pool_unittest.cc
+++ b/base/threading/sequenced_worker_pool_unittest.cc
@@ -321,9 +321,9 @@
     // workers to be created.
     ThreadBlocker blocker;
     for (size_t i = 0; i < kNumWorkerThreads; i++) {
-      pool()->PostWorkerTask(FROM_HERE,
-                             base::Bind(&TestTracker::BlockTask,
-                                        tracker(), -1, &blocker));
+      pool()->PostWorkerTask(
+          FROM_HERE,
+          base::BindOnce(&TestTracker::BlockTask, tracker(), -1, &blocker));
     }
     tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
 
@@ -383,7 +383,7 @@
 TEST_P(SequencedWorkerPoolTest, DelayedTaskDuringShutdown) {
   // Post something to verify the pool is started up.
   EXPECT_TRUE(pool()->PostTask(
-      FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 1)));
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 1)));
 
   scoped_refptr<base::RefCountedData<bool> > deleted_flag(
       new base::RefCountedData<bool>(false));
@@ -392,8 +392,8 @@
   // Post something that shouldn't run.
   EXPECT_TRUE(pool()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&ShouldNotRun,
-                 make_scoped_refptr(new DeletionHelper(deleted_flag))),
+      base::BindOnce(&ShouldNotRun,
+                     make_scoped_refptr(new DeletionHelper(deleted_flag))),
       TestTimeouts::action_timeout()));
 
   std::vector<int> completion_sequence = tracker()->WaitUntilTasksComplete(1);
@@ -453,12 +453,12 @@
 // threads) runs them all.
 TEST_P(SequencedWorkerPoolTest, LotsOfTasks) {
   pool()->PostWorkerTask(FROM_HERE,
-                         base::Bind(&TestTracker::SlowTask, tracker(), 0));
+                         base::BindOnce(&TestTracker::SlowTask, tracker(), 0));
 
   const size_t kNumTasks = 20;
   for (size_t i = 1; i < kNumTasks; i++) {
-    pool()->PostWorkerTask(FROM_HERE,
-                           base::Bind(&TestTracker::FastTask, tracker(), i));
+    pool()->PostWorkerTask(
+        FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), i));
   }
 
   std::vector<int> result = tracker()->WaitUntilTasksComplete(kNumTasks);
@@ -498,8 +498,8 @@
   ThreadBlocker background_blocker;
   for (size_t i = 0; i < kNumBackgroundTasks; i++) {
     pool()->PostWorkerTask(FROM_HERE,
-                           base::Bind(&TestTracker::BlockTask,
-                                      tracker(), i, &background_blocker));
+                           base::BindOnce(&TestTracker::BlockTask, tracker(), i,
+                                          &background_blocker));
   }
   tracker()->WaitUntilTasksBlocked(kNumBackgroundTasks);
 
@@ -511,10 +511,10 @@
   SequencedWorkerPool::SequenceToken token1 = pool()->GetSequenceToken();
   pool()->PostSequencedWorkerTask(
       token1, FROM_HERE,
-      base::Bind(&TestTracker::BlockTask, tracker(), 100, &blocker));
+      base::BindOnce(&TestTracker::BlockTask, tracker(), 100, &blocker));
   pool()->PostSequencedWorkerTask(
       token1, FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 101));
+      base::BindOnce(&TestTracker::FastTask, tracker(), 101));
   EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
 
   // Create another two tasks as above with a different token. These will be
@@ -522,10 +522,10 @@
   SequencedWorkerPool::SequenceToken token2 = pool()->GetSequenceToken();
   pool()->PostSequencedWorkerTask(
       token2, FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 200));
+      base::BindOnce(&TestTracker::FastTask, tracker(), 200));
   pool()->PostSequencedWorkerTask(
       token2, FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 201));
+      base::BindOnce(&TestTracker::FastTask, tracker(), 201));
   EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
 
   // Let one background task complete. This should then let both tasks of
@@ -558,9 +558,8 @@
   EnsureAllWorkersCreated();
   ThreadBlocker blocker;
   for (size_t i = 0; i < kNumWorkerThreads; i++) {
-    pool()->PostWorkerTask(FROM_HERE,
-                           base::Bind(&TestTracker::BlockTask,
-                                      tracker(), i, &blocker));
+    pool()->PostWorkerTask(FROM_HERE, base::BindOnce(&TestTracker::BlockTask,
+                                                     tracker(), i, &blocker));
   }
   tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
 
@@ -585,16 +584,13 @@
 
   // No further tasks, regardless of shutdown mode, should be allowed.
   EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 100),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 100),
       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
   EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 101),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 101),
       SequencedWorkerPool::SKIP_ON_SHUTDOWN));
   EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 102),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 102),
       SequencedWorkerPool::BLOCK_SHUTDOWN));
 
   ASSERT_EQ(old_has_work_call_count, has_work_call_count());
@@ -611,7 +607,7 @@
   for (int i = 0; i < kNumBlockTasks; ++i) {
     EXPECT_TRUE(pool()->PostWorkerTask(
         FROM_HERE,
-        base::Bind(&TestTracker::BlockTask, tracker(), i, &blocker)));
+        base::BindOnce(&TestTracker::BlockTask, tracker(), i, &blocker)));
   }
   tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
 
@@ -621,8 +617,9 @@
   const int kNumQueuedTasks = static_cast<int>(kNumWorkerThreads);
   for (int i = 0; i < kNumQueuedTasks; ++i) {
     EXPECT_TRUE(pool()->PostWorkerTaskWithShutdownBehavior(
-        FROM_HERE, base::Bind(&TestTracker::PostAdditionalTasks, tracker(), i,
-                              base::RetainedRef(pool()), false),
+        FROM_HERE,
+        base::BindOnce(&TestTracker::PostAdditionalTasks, tracker(), i,
+                       base::RetainedRef(pool()), false),
         SequencedWorkerPool::BLOCK_SHUTDOWN));
   }
 
@@ -674,7 +671,7 @@
   for (int i = 0; i < kNumBlockTasks; ++i) {
     EXPECT_TRUE(pool()->PostWorkerTask(
         FROM_HERE,
-        base::Bind(&TestTracker::BlockTask, tracker(), i, &blocker)));
+        base::BindOnce(&TestTracker::BlockTask, tracker(), i, &blocker)));
   }
   tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
 
@@ -728,24 +725,20 @@
   EnsureAllWorkersCreated();
   ThreadBlocker blocker;
   for (size_t i = 0; i < kNumWorkerThreads; i++) {
-    pool()->PostWorkerTask(FROM_HERE,
-                           base::Bind(&TestTracker::BlockTask,
-                                      tracker(), i, &blocker));
+    pool()->PostWorkerTask(FROM_HERE, base::BindOnce(&TestTracker::BlockTask,
+                                                     tracker(), i, &blocker));
   }
   tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
 
   // Create some tasks with different shutdown modes.
   pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 100),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 100),
       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
   pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 101),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 101),
       SequencedWorkerPool::SKIP_ON_SHUTDOWN);
   pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 102),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 102),
       SequencedWorkerPool::BLOCK_SHUTDOWN);
 
   // Shutdown the worker pool. This should discard all non-blocking tasks.
@@ -778,17 +771,12 @@
   ThreadBlocker blocker;
   pool()->PostWorkerTaskWithShutdownBehavior(
       FROM_HERE,
-      base::Bind(&TestTracker::BlockTask,
-                 tracker(), 0, &blocker),
+      base::BindOnce(&TestTracker::BlockTask, tracker(), 0, &blocker),
       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
-  runner->PostTask(
-      FROM_HERE,
-      base::Bind(&TestTracker::BlockTask,
-                 tracker(), 1, &blocker));
-  sequenced_runner->PostTask(
-      FROM_HERE,
-      base::Bind(&TestTracker::BlockTask,
-                 tracker(), 2, &blocker));
+  runner->PostTask(FROM_HERE, base::BindOnce(&TestTracker::BlockTask, tracker(),
+                                             1, &blocker));
+  sequenced_runner->PostTask(FROM_HERE, base::BindOnce(&TestTracker::BlockTask,
+                                                       tracker(), 2, &blocker));
 
   tracker()->WaitUntilTasksBlocked(3);
 
@@ -802,12 +790,12 @@
 
   // Posting more tasks should fail.
   EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 0),
       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
   EXPECT_FALSE(runner->PostTask(
-      FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 0)));
   EXPECT_FALSE(sequenced_runner->PostTask(
-      FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 0)));
 
   // Continue the background thread and make sure the tasks can complete.
   blocker.Unblock(3);
@@ -836,7 +824,7 @@
   for (size_t i = 0; i < kNumWorkerThreads; i++) {
     pool()->PostWorkerTaskWithShutdownBehavior(
         FROM_HERE,
-        base::Bind(&TestTracker::BlockTask, tracker(), i, &blocker),
+        base::BindOnce(&TestTracker::BlockTask, tracker(), i, &blocker),
         SequencedWorkerPool::SKIP_ON_SHUTDOWN);
   }
   tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
@@ -845,8 +833,7 @@
   // executed once Shutdown() has been called.
   pool()->PostWorkerTaskWithShutdownBehavior(
       FROM_HERE,
-      base::Bind(&TestTracker::BlockTask,
-                 tracker(), 0, &blocker),
+      base::BindOnce(&TestTracker::BlockTask, tracker(), 0, &blocker),
       SequencedWorkerPool::SKIP_ON_SHUTDOWN);
 
   // This callback will only be invoked if SKIP_ON_SHUTDOWN tasks that have
@@ -934,10 +921,10 @@
   // - unused_pool_owner.pool()->RunsTasksOnCurrentThread() returns false.
   sequenced_task_runner_1->PostTask(
       FROM_HERE,
-      base::Bind(&VerifyRunsTasksOnCurrentThread, RedirectedToTaskScheduler(),
-                 sequenced_task_runner_1, sequenced_task_runner_2,
-                 base::RetainedRef(pool()),
-                 base::RetainedRef(unused_pool_owner.pool())));
+      base::BindOnce(&VerifyRunsTasksOnCurrentThread,
+                     RedirectedToTaskScheduler(), sequenced_task_runner_1,
+                     sequenced_task_runner_2, base::RetainedRef(pool()),
+                     base::RetainedRef(unused_pool_owner.pool())));
   // From a task posted to |unsequenced_task_runner|:
   // - unsequenced_task_runner->RunsTasksOnCurrentThread() returns true.
   // - sequenced_task_runner_1->RunsTasksOnCurrentThread() returns false.
@@ -945,10 +932,10 @@
   // - unused_pool_owner.pool()->RunsTasksOnCurrentThread() returns false.
   unsequenced_task_runner->PostTask(
       FROM_HERE,
-      base::Bind(&VerifyRunsTasksOnCurrentThread, RedirectedToTaskScheduler(),
-                 unsequenced_task_runner, sequenced_task_runner_1,
-                 base::RetainedRef(pool()),
-                 base::RetainedRef(unused_pool_owner.pool())));
+      base::BindOnce(&VerifyRunsTasksOnCurrentThread,
+                     RedirectedToTaskScheduler(), unsequenced_task_runner,
+                     sequenced_task_runner_1, base::RetainedRef(pool()),
+                     base::RetainedRef(unused_pool_owner.pool())));
 }
 
 // Checks that tasks are destroyed in the right context during shutdown. If a
@@ -1004,19 +991,18 @@
   // Queue up a bunch of work, including  a long delayed task and
   // a task that produces additional tasks as an artifact.
   pool()->PostDelayedWorkerTask(
-      FROM_HERE,
-      base::Bind(&TestTracker::FastTask, tracker(), 0),
+      FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 0),
       TimeDelta::FromMinutes(5));
   pool()->PostWorkerTask(FROM_HERE,
-                         base::Bind(&TestTracker::SlowTask, tracker(), 0));
+                         base::BindOnce(&TestTracker::SlowTask, tracker(), 0));
   const size_t kNumFastTasks = 20;
   for (size_t i = 0; i < kNumFastTasks; i++) {
-    pool()->PostWorkerTask(FROM_HERE,
-                           base::Bind(&TestTracker::FastTask, tracker(), 0));
+    pool()->PostWorkerTask(
+        FROM_HERE, base::BindOnce(&TestTracker::FastTask, tracker(), 0));
   }
   pool()->PostWorkerTask(
-      FROM_HERE, base::Bind(&TestTracker::PostAdditionalTasks, tracker(), 0,
-                            base::RetainedRef(pool()), true));
+      FROM_HERE, base::BindOnce(&TestTracker::PostAdditionalTasks, tracker(), 0,
+                                base::RetainedRef(pool()), true));
 
   // We expect all except the delayed task to have been run. We verify all
   // closures have been deleted by looking at the refcount of the
@@ -1086,14 +1072,14 @@
   SequencedWorkerPool::SequenceToken token2 = pool()->GetSequenceToken();
   pool()->PostSequencedWorkerTask(
       token1, FROM_HERE,
-      base::Bind(&CheckWorkerPoolAndSequenceToken, pool(), token1));
+      base::BindOnce(&CheckWorkerPoolAndSequenceToken, pool(), token1));
   pool()->PostSequencedWorkerTask(
       token2, FROM_HERE,
-      base::Bind(&CheckWorkerPoolAndSequenceToken, pool(), token2));
+      base::BindOnce(&CheckWorkerPoolAndSequenceToken, pool(), token2));
 
-  pool()->PostWorkerTask(FROM_HERE,
-                         base::Bind(&CheckWorkerPoolAndSequenceToken, pool(),
-                                    SequencedWorkerPool::SequenceToken()));
+  pool()->PostWorkerTask(
+      FROM_HERE, base::BindOnce(&CheckWorkerPoolAndSequenceToken, pool(),
+                                SequencedWorkerPool::SequenceToken()));
 
   pool()->FlushForTesting();
 }
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 0aeed2a9..75c911b7 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -156,7 +156,7 @@
   WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
                      WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner()->PostTask(FROM_HERE,
-                          Bind(&WaitableEvent::Signal, Unretained(&done)));
+                          BindOnce(&WaitableEvent::Signal, Unretained(&done)));
   done.Wait();
 }
 
@@ -210,7 +210,7 @@
   }
 
   task_runner()->PostTask(
-      FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this)));
+      FROM_HERE, base::BindOnce(&Thread::ThreadQuitHelper, Unretained(this)));
 }
 
 void Thread::DetachFromSequence() {
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc
index ef4e8f715..32b42896 100644
--- a/base/threading/thread_perftest.cc
+++ b/base/threading/thread_perftest.cc
@@ -67,8 +67,8 @@
                              WaitableEvent::InitialState::NOT_SIGNALED);
     base::ThreadTicks ticks;
     thread.task_runner()->PostTask(
-        FROM_HERE, base::Bind(&ThreadPerfTest::TimeOnThread,
-                              base::Unretained(this), &ticks, &done));
+        FROM_HERE, base::BindOnce(&ThreadPerfTest::TimeOnThread,
+                                  base::Unretained(this), &ticks, &done));
     done.Wait();
     return ticks;
   }
@@ -138,8 +138,8 @@
       return;
     }
     NextThread(hops)->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&ThreadPerfTest::PingPong, base::Unretained(this),
-                              hops - 1));
+        FROM_HERE, base::BindOnce(&ThreadPerfTest::PingPong,
+                                  base::Unretained(this), hops - 1));
   }
 };
 
@@ -210,8 +210,8 @@
     remaining_hops_ = hops;
     for (size_t i = 0; i < threads_.size(); i++) {
       threads_[i]->task_runner()->PostTask(
-          FROM_HERE, base::Bind(&EventPerfTest::WaitAndSignalOnThread,
-                                base::Unretained(this), i));
+          FROM_HERE, base::BindOnce(&EventPerfTest::WaitAndSignalOnThread,
+                                    base::Unretained(this), i));
     }
 
     // Kick off the Signal ping-ponging.
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index 0cb964e..e0371dd 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -155,8 +155,9 @@
 
   base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
-  a.task_runner()->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
-                                                  base::Unretained(&event)));
+  a.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
   event.Wait();
 }
 
@@ -188,9 +189,9 @@
   base::WaitableEvent block_event(
       base::WaitableEvent::ResetPolicy::AUTOMATIC,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  a->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&block_event)));
+  a->task_runner()->PostTask(FROM_HERE,
+                             base::BindOnce(&base::WaitableEvent::Wait,
+                                            base::Unretained(&block_event)));
 
   a->StopSoon();
   EXPECT_TRUE(a->IsRunning());
@@ -215,11 +216,11 @@
     // destroyed.  We do this by dispatching a sleep event before the
     // event that will toggle our sentinel value.
     a.task_runner()->PostTask(
-        FROM_HERE, base::Bind(static_cast<void (*)(base::TimeDelta)>(
-                                  &base::PlatformThread::Sleep),
-                              base::TimeDelta::FromMilliseconds(20)));
+        FROM_HERE, base::BindOnce(static_cast<void (*)(base::TimeDelta)>(
+                                      &base::PlatformThread::Sleep),
+                                  base::TimeDelta::FromMilliseconds(20)));
     a.task_runner()->PostTask(FROM_HERE,
-                              base::Bind(&ToggleValue, &was_invoked));
+                              base::BindOnce(&ToggleValue, &was_invoked));
   }
   EXPECT_TRUE(was_invoked);
 }
@@ -285,8 +286,8 @@
   b.Start();
   EXPECT_DCHECK_DEATH({
     // Stopping |a| on |b| isn't allowed.
-    b.task_runner()->PostTask(FROM_HERE,
-                              base::Bind(&Thread::Stop, base::Unretained(&a)));
+    b.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a)));
     // Block here so the DCHECK on |b| always happens in this scope.
     base::PlatformThread::Sleep(base::TimeDelta::Max());
   });
@@ -307,7 +308,7 @@
   // a->DetachFromSequence() should allow |b| to use |a|'s Thread API.
   a->DetachFromSequence();
   b.task_runner()->PostTask(
-      FROM_HERE, base::Bind(
+      FROM_HERE, base::BindOnce(
                      [](std::unique_ptr<Thread> thread_to_stop,
                         base::WaitableEvent* event_to_signal) -> void {
                        thread_to_stop->Stop();
@@ -361,9 +362,9 @@
   base::WaitableEvent last_task_event(
       base::WaitableEvent::ResetPolicy::AUTOMATIC,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  a->task_runner()->PostTask(FROM_HERE,
-                            base::Bind(&base::WaitableEvent::Signal,
-                                       base::Unretained(&last_task_event)));
+  a->task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
+                                base::Unretained(&last_task_event)));
 
   // StopSoon() is non-blocking, Yield() to |a|, wait for last task to be
   // processed and a little more for QuitWhenIdle() to unwind before considering
@@ -399,7 +400,8 @@
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
   base::PlatformThreadId id_from_new_thread;
   a.task_runner()->PostTask(
-      FROM_HERE, base::Bind(ReturnThreadId, &a, &id_from_new_thread, &event));
+      FROM_HERE,
+      base::BindOnce(ReturnThreadId, &a, &id_from_new_thread, &event));
 
   // Call GetThreadId() on the current thread before calling event.Wait() so
   // that this test can find a race issue with TSAN.
@@ -458,8 +460,9 @@
     // Register an observer that writes into |captured_events| once the
     // thread's message loop is destroyed.
     t.task_runner()->PostTask(
-        FROM_HERE, base::Bind(&RegisterDestructionObserver,
-                              base::Unretained(&loop_destruction_observer)));
+        FROM_HERE,
+        base::BindOnce(&RegisterDestructionObserver,
+                       base::Unretained(&loop_destruction_observer)));
 
     // Upon leaving this scope, the thread is deleted.
   }
@@ -503,7 +506,8 @@
 
   for (size_t i = 0; i < kNumSleepTasks; ++i) {
     a.task_runner()->PostTask(
-        FROM_HERE, base::Bind(&base::PlatformThread::Sleep, kSleepPerTestTask));
+        FROM_HERE,
+        base::BindOnce(&base::PlatformThread::Sleep, kSleepPerTestTask));
   }
 
   // All tasks should have executed, as reflected by the elapsed time.
@@ -557,7 +561,7 @@
 
   bool ran = false;
   a.task_runner()->PostTask(
-      FROM_HERE, base::Bind([](bool* toggled) { *toggled = true; }, &ran));
+      FROM_HERE, base::BindOnce([](bool* toggled) { *toggled = true; }, &ran));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(ran);
 
diff --git a/base/threading/worker_pool_unittest.cc b/base/threading/worker_pool_unittest.cc
index ef4bed13..a6d2e75 100644
--- a/base/threading/worker_pool_unittest.cc
+++ b/base/threading/worker_pool_unittest.cc
@@ -34,10 +34,9 @@
   void RunTest() {
     ASSERT_TRUE(thread_checker_.CalledOnValidThread());
     WorkerPool::PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&PostTaskAndReplyTester::OnWorkerThread, this),
-      base::Bind(&PostTaskAndReplyTester::OnOriginalThread, this),
-      false);
+        FROM_HERE,
+        base::BindOnce(&PostTaskAndReplyTester::OnWorkerThread, this),
+        base::BindOnce(&PostTaskAndReplyTester::OnOriginalThread, this), false);
 
     test_event_.Wait();
   }
@@ -77,13 +76,13 @@
   WaitableEvent long_test_event(WaitableEvent::ResetPolicy::AUTOMATIC,
                                 WaitableEvent::InitialState::NOT_SIGNALED);
 
+  WorkerPool::PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaitableEvent::Signal, base::Unretained(&test_event)),
+      false);
   WorkerPool::PostTask(FROM_HERE,
-                       base::Bind(&WaitableEvent::Signal,
-                                  base::Unretained(&test_event)),
-                       false);
-  WorkerPool::PostTask(FROM_HERE,
-                       base::Bind(&WaitableEvent::Signal,
-                                  base::Unretained(&long_test_event)),
+                       base::BindOnce(&WaitableEvent::Signal,
+                                      base::Unretained(&long_test_event)),
                        true);
 
   test_event.Wait();
diff --git a/base/timer/timer.cc b/base/timer/timer.cc
index 6ec18f1..31eec1b 100644
--- a/base/timer/timer.cc
+++ b/base/timer/timer.cc
@@ -170,13 +170,16 @@
   is_running_ = true;
   scheduled_task_ = new BaseTimerTaskInternal(this);
   if (delay > TimeDelta::FromMicroseconds(0)) {
-    GetTaskRunner()->PostDelayedTask(posted_from_,
-        base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
+    GetTaskRunner()->PostDelayedTask(
+        posted_from_,
+        base::BindOnce(&BaseTimerTaskInternal::Run,
+                       base::Owned(scheduled_task_)),
         delay);
     scheduled_run_time_ = desired_run_time_ = Now() + delay;
   } else {
     GetTaskRunner()->PostTask(posted_from_,
-        base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)));
+                              base::BindOnce(&BaseTimerTaskInternal::Run,
+                                             base::Owned(scheduled_task_)));
     scheduled_run_time_ = desired_run_time_ = TimeTicks();
   }
   // Remember the thread ID that posts the first task -- this will be verified
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 50a7490..bced055 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -349,8 +349,9 @@
     // after the OnTraceLogEnabled()
     if (options.is_fast_polling_supported && dump_thread_) {
       dump_thread_->task_runner()->PostTask(
-          FROM_HERE, Bind(&MemoryDumpManager::RegisterPollingMDPOnDumpThread,
-                          Unretained(this), mdpinfo));
+          FROM_HERE,
+          BindOnce(&MemoryDumpManager::RegisterPollingMDPOnDumpThread,
+                   Unretained(this), mdpinfo));
     }
   }
 
@@ -420,8 +421,9 @@
   if ((*mdp_iter)->options.is_fast_polling_supported && dump_thread_) {
     DCHECK(take_mdp_ownership_and_delete_async);
     dump_thread_->task_runner()->PostTask(
-        FROM_HERE, Bind(&MemoryDumpManager::UnregisterPollingMDPOnDumpThread,
-                        Unretained(this), *mdp_iter));
+        FROM_HERE,
+        BindOnce(&MemoryDumpManager::UnregisterPollingMDPOnDumpThread,
+                 Unretained(this), *mdp_iter));
   }
 
   // The MDPInfo instance can still be referenced by the
@@ -616,8 +618,8 @@
   }
 
   bool did_post_task = task_runner->PostTask(
-      FROM_HERE, Bind(&MemoryDumpManager::InvokeOnMemoryDump, Unretained(this),
-                      Unretained(pmd_async_state.get())));
+      FROM_HERE, BindOnce(&MemoryDumpManager::InvokeOnMemoryDump,
+                          Unretained(this), Unretained(pmd_async_state.get())));
 
   if (did_post_task) {
     // Ownership is tranferred to InvokeOnMemoryDump().
@@ -761,8 +763,8 @@
     scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
         pmd_async_state->callback_task_runner;
     callback_task_runner->PostTask(
-        FROM_HERE, Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
-                        Passed(&pmd_async_state)));
+        FROM_HERE, BindOnce(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
+                            Passed(&pmd_async_state)));
     return;
   }
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index e126edd..cb7af62 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -109,8 +109,8 @@
   base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
                             WaitableEvent::InitialState::NOT_SIGNALED);
   task_runner->PostTask(from_here, std::move(task));
-  task_runner->PostTask(
-      FROM_HERE, base::Bind(&WaitableEvent::Signal, base::Unretained(&event)));
+  task_runner->PostTask(FROM_HERE, base::BindOnce(&WaitableEvent::Signal,
+                                                  base::Unretained(&event)));
   // The SequencedTaskRunner guarantees that |event| will only be signaled after
   // |task| is executed.
   event.Wait();
@@ -734,10 +734,10 @@
         threads[other_idx]->task_runner();
     MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
     auto on_dump = [this, other_runner, other_mdp, &on_memory_dump_call_count](
-        const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+                       const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
       PostTaskAndWait(FROM_HERE, other_runner.get(),
-                      base::Bind(&MemoryDumpManager::UnregisterDumpProvider,
-                                 base::Unretained(&*mdm_), other_mdp));
+                      base::BindOnce(&MemoryDumpManager::UnregisterDumpProvider,
+                                     base::Unretained(&*mdm_), other_mdp));
       on_memory_dump_call_count++;
       return true;
     };
@@ -855,10 +855,10 @@
     scoped_refptr<SequencedTaskRunner> main_runner =
         SequencedTaskRunnerHandle::Get();
     auto on_dump = [other_thread, main_runner, &on_memory_dump_call_count](
-        const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+                       const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
       PostTaskAndWait(
           FROM_HERE, main_runner.get(),
-          base::Bind(&TestIOThread::Stop, base::Unretained(other_thread)));
+          base::BindOnce(&TestIOThread::Stop, base::Unretained(other_thread)));
       on_memory_dump_call_count++;
       return true;
     };
@@ -1210,7 +1210,7 @@
     TestIOThread thread_for_unregistration(TestIOThread::kAutoStart);
     PostTaskAndWait(
         FROM_HERE, thread_for_unregistration.task_runner().get(),
-        base::Bind(
+        base::BindOnce(
             &MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon,
             base::Unretained(MemoryDumpManager::GetInstance()),
             base::Passed(std::unique_ptr<MemoryDumpProvider>(std::move(mdp)))));
diff --git a/base/trace_event/memory_dump_scheduler.cc b/base/trace_event/memory_dump_scheduler.cc
index 150feb8..91d70b78 100644
--- a/base/trace_event/memory_dump_scheduler.cc
+++ b/base/trace_event/memory_dump_scheduler.cc
@@ -105,8 +105,8 @@
   polling_state_->ResetTotals();
 
   polling_task_runner_->PostTask(
-      FROM_HERE,
-      Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)));
+      FROM_HERE, BindOnce(&MemoryDumpScheduler::PollMemoryOnPollingThread,
+                          Unretained(this)));
 }
 
 void MemoryDumpScheduler::NotifyDumpTriggered() {
@@ -114,7 +114,7 @@
       !polling_task_runner_->RunsTasksOnCurrentThread()) {
     polling_task_runner_->PostTask(
         FROM_HERE,
-        Bind(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this)));
+        BindOnce(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this)));
     return;
   }
 
@@ -136,8 +136,8 @@
   if (polling_task_runner_) {
     DCHECK(polling_state_);
     polling_task_runner_->PostTask(
-        FROM_HERE, Bind(&MemoryDumpScheduler::DisablePollingOnPollingThread,
-                        Unretained(this)));
+        FROM_HERE, BindOnce(&MemoryDumpScheduler::DisablePollingOnPollingThread,
+                            Unretained(this)));
     polling_task_runner_ = nullptr;
   }
   is_setup_ = false;
@@ -197,7 +197,8 @@
   // TODO(ssid): Use RequestSchedulerCallback, crbug.com/607533.
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)),
+      BindOnce(&MemoryDumpScheduler::PollMemoryOnPollingThread,
+               Unretained(this)),
       TimeDelta::FromMilliseconds(polling_state_->polling_interval_ms));
 }
 
diff --git a/base/trace_event/memory_peak_detector.cc b/base/trace_event/memory_peak_detector.cc
index c361037..2423fa336 100644
--- a/base/trace_event/memory_peak_detector.cc
+++ b/base/trace_event/memory_peak_detector.cc
@@ -4,13 +4,17 @@
 
 #include "base/trace_event/memory_peak_detector.h"
 
-#include <stdint.h>
+#include <algorithm>
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_dump_provider_info.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace trace_event {
@@ -24,7 +28,6 @@
 MemoryPeakDetector::MemoryPeakDetector()
     : generation_(0),
       state_(NOT_INITIALIZED),
-      polling_interval_ms_(0),
       poll_tasks_count_for_testing_(0) {}
 
 MemoryPeakDetector::~MemoryPeakDetector() {
@@ -46,6 +49,19 @@
   task_runner_ = task_runner;
   on_peak_detected_callback_ = on_peak_detected_callback;
   state_ = DISABLED;
+  config_ = {};
+  ResetPollHistory();
+
+  static_threshold_bytes_ = 0;
+#if !defined(OS_NACL)
+  // Set threshold to 1% of total system memory.
+  static_threshold_bytes_ =
+      static_cast<uint64_t>(SysInfo::AmountOfPhysicalMemory()) / 100;
+#endif
+  // Fallback, mostly for test environments where AmountOfPhysicalMemory() is
+  // broken.
+  static_threshold_bytes_ =
+      std::max(static_threshold_bytes_, static_cast<uint64_t>(5 * 1024 * 1024));
 }
 
 void MemoryPeakDetector::TearDown() {
@@ -57,9 +73,13 @@
   task_runner_ = nullptr;
 }
 
-void MemoryPeakDetector::Start() {
-  task_runner_->PostTask(
-      FROM_HERE, Bind(&MemoryPeakDetector::StartInternal, Unretained(this)));
+void MemoryPeakDetector::Start(MemoryPeakDetector::Config config) {
+  if (!config.polling_interval_ms) {
+    NOTREACHED();
+    return;
+  }
+  task_runner_->PostTask(FROM_HERE, Bind(&MemoryPeakDetector::StartInternal,
+                                         Unretained(this), config));
 }
 
 void MemoryPeakDetector::Stop() {
@@ -67,28 +87,36 @@
       FROM_HERE, Bind(&MemoryPeakDetector::StopInternal, Unretained(this)));
 }
 
-void MemoryPeakDetector::NotifyMemoryDumpProvidersChanged() {
-  // It is possible to call this before the first Setup() call, in which case
-  // we want to just make this a noop. The next Start() will fetch the MDP list.
+void MemoryPeakDetector::Throttle() {
   if (!task_runner_)
-    return;
+    return;  // Can be called before Setup().
+  task_runner_->PostTask(
+      FROM_HERE, Bind(&MemoryPeakDetector::ResetPollHistory, Unretained(this),
+                      true /* keep_last_sample */));
+}
+
+void MemoryPeakDetector::NotifyMemoryDumpProvidersChanged() {
+  if (!task_runner_)
+    return;  // Can be called before Setup().
   task_runner_->PostTask(
       FROM_HERE,
       Bind(&MemoryPeakDetector::ReloadDumpProvidersAndStartPollingIfNeeded,
            Unretained(this)));
 }
 
-void MemoryPeakDetector::StartInternal() {
+void MemoryPeakDetector::StartInternal(MemoryPeakDetector::Config config) {
   DCHECK_EQ(DISABLED, state_);
   state_ = ENABLED;
-  polling_interval_ms_ = 1;  // TODO(primiano): temporary until next CL.
+  config_ = config;
+  ResetPollHistory();
 
-  // If there are any dump providers available, NotifyMemoryDumpProvidersChanged
-  // will fetch them and start the polling. Otherwise this will remain in the
-  // ENABLED state and the actual polling will start on the next call to
+  // If there are any dump providers available,
+  // NotifyMemoryDumpProvidersChanged will fetch them and start the polling.
+  // Otherwise this will remain in the ENABLED state and the actual polling
+  // will start on the next call to
   // ReloadDumpProvidersAndStartPollingIfNeeded().
-  // Depending on the sandbox model, it is possible that no polling-capable dump
-  // providers will be ever available.
+  // Depending on the sandbox model, it is possible that no polling-capable
+  // dump providers will be ever available.
   ReloadDumpProvidersAndStartPollingIfNeeded();
 }
 
@@ -132,7 +160,7 @@
 }
 
 void MemoryPeakDetector::PollMemoryAndDetectPeak(uint32_t expected_generation) {
-  if (state_ != RUNNING || expected_generation != generation_)
+  if (state_ != RUNNING || generation_ != expected_generation)
     return;
 
   // We should never end up in a situation where state_ == RUNNING but all dump
@@ -140,24 +168,107 @@
   DCHECK(!dump_providers_.empty());
 
   poll_tasks_count_for_testing_++;
-  uint64_t memory_total = 0;
+  uint64_t polled_mem_bytes = 0;
   for (const scoped_refptr<MemoryDumpProviderInfo>& mdp_info :
        dump_providers_) {
     DCHECK(mdp_info->options.is_fast_polling_supported);
     uint64_t value = 0;
     mdp_info->dump_provider->PollFastMemoryTotal(&value);
-    memory_total += value;
+    polled_mem_bytes += value;
   }
-  ignore_result(memory_total);  // TODO(primiano): temporary until next CL.
+  if (config_.enable_verbose_poll_tracing) {
+    TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB",
+                   polled_mem_bytes / 1024 / 1024);
+  }
 
-  // TODO(primiano): Move actual peak detection logic from the
-  // MemoryDumpScheduler in next CLs.
+  // Peak detection logic. Design doc: https://goo.gl/0kOU4A .
+  bool is_peak = false;
+  if (skip_polls_ > 0) {
+    skip_polls_--;
+  } else if (last_dump_memory_total_ == 0) {
+    last_dump_memory_total_ = polled_mem_bytes;
+  } else if (polled_mem_bytes > 0) {
+    int64_t diff_from_last_dump = polled_mem_bytes - last_dump_memory_total_;
 
+    DCHECK_GT(static_threshold_bytes_, 0u);
+    is_peak =
+        diff_from_last_dump > static_cast<int64_t>(static_threshold_bytes_);
+
+    if (!is_peak)
+      is_peak = DetectPeakUsingSlidingWindowStddev(polled_mem_bytes);
+  }
+
+  DCHECK_GT(config_.polling_interval_ms, 0u);
   SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       Bind(&MemoryPeakDetector::PollMemoryAndDetectPeak, Unretained(this),
            expected_generation),
-      TimeDelta::FromMilliseconds(polling_interval_ms_));
+      TimeDelta::FromMilliseconds(config_.polling_interval_ms));
+
+  if (!is_peak)
+    return;
+  TRACE_EVENT_INSTANT1(MemoryDumpManager::kTraceCategory,
+                       "Peak memory detected", TRACE_EVENT_SCOPE_PROCESS,
+                       "PolledMemoryMB", polled_mem_bytes / 1024 / 1024);
+  ResetPollHistory(true /* keep_last_sample */);
+  last_dump_memory_total_ = polled_mem_bytes;
+  on_peak_detected_callback_.Run();
+}
+
+bool MemoryPeakDetector::DetectPeakUsingSlidingWindowStddev(
+    uint64_t polled_mem_bytes) {
+  DCHECK(polled_mem_bytes);
+  samples_bytes_[samples_index_] = polled_mem_bytes;
+  samples_index_ = (samples_index_ + 1) % kSlidingWindowNumSamples;
+  float mean = 0;
+  for (uint32_t i = 0; i < kSlidingWindowNumSamples; ++i) {
+    if (samples_bytes_[i] == 0)
+      return false;  // Not enough samples to detect peaks.
+    mean += samples_bytes_[i];
+  }
+  mean /= kSlidingWindowNumSamples;
+  float variance = 0;
+  for (uint32_t i = 0; i < kSlidingWindowNumSamples; ++i) {
+    const float deviation = samples_bytes_[i] - mean;
+    variance += deviation * deviation;
+  }
+  variance /= kSlidingWindowNumSamples;
+
+  // If stddev is less than 0.2% then we consider that the process is inactive.
+  if (variance < (mean / 500) * (mean / 500))
+    return false;
+
+  // (mean + 3.69 * stddev) corresponds to a value that is higher than current
+  // sample with 99.99% probability.
+  const float cur_sample_deviation = polled_mem_bytes - mean;
+  return cur_sample_deviation * cur_sample_deviation > (3.69 * 3.69 * variance);
+}
+
+void MemoryPeakDetector::ResetPollHistory(bool keep_last_sample) {
+  // TODO(primiano,ssid): this logic should probably be revisited. In the case
+  // of Android, the browser process sees the total of all processes memory in
+  // the same peak detector instance. Perhaps the best thing to do here is to
+  // keep the window of samples around and just bump the skip_polls_.
+  last_dump_memory_total_ = 0;
+  if (keep_last_sample) {
+    const uint32_t prev_index =
+        samples_index_ > 0 ? samples_index_ - 1 : kSlidingWindowNumSamples - 1;
+    last_dump_memory_total_ = samples_bytes_[prev_index];
+  }
+  memset(samples_bytes_, 0, sizeof(samples_bytes_));
+  samples_index_ = 0;
+  skip_polls_ = 0;
+  if (config_.polling_interval_ms > 0) {
+    skip_polls_ =
+        (config_.min_time_between_peaks_ms + config_.polling_interval_ms - 1) /
+        config_.polling_interval_ms;
+  }
+}
+
+void MemoryPeakDetector::SetStaticThresholdForTesting(
+    uint64_t static_threshold_bytes) {
+  DCHECK_EQ(DISABLED, state_);
+  static_threshold_bytes_ = static_threshold_bytes;
 }
 
 }  // namespace trace_event
diff --git a/base/trace_event/memory_peak_detector.h b/base/trace_event/memory_peak_detector.h
index b914295..d2da9ca 100644
--- a/base/trace_event/memory_peak_detector.h
+++ b/base/trace_event/memory_peak_detector.h
@@ -23,6 +23,13 @@
 
 struct MemoryDumpProviderInfo;
 
+// Detects temporally local memory peaks. Peak detection is based on
+// continuously querying memory usage using MemoryDumpprovider(s) that support
+// fast polling (e.g., ProcessMetricsDumpProvider which under the hoods reads
+// /proc/PID/statm on Linux) and using a cobination of:
+// - An static threshold (currently 1% of total system memory).
+// - Sliding window stddev analysis.
+// Design doc: https://goo.gl/0kOU4A .
 // This class is NOT thread-safe, the caller has to ensure linearization of
 // the calls to the public methods. In any case, the public methods do NOT have
 // to be called from the |task_runner| on which the polling tasks run.
@@ -39,6 +46,21 @@
     RUNNING  // After Start(). The PollMemoryAndDetectPeak() task is scheduled.
   };
 
+  // Peak detector configuration, passed to Start().
+  struct Config {
+    // The rate at which memory will be polled. Polls will happen on the task
+    // runner passed to Setup().
+    uint32_t polling_interval_ms;
+
+    // Two consecutive peak detection callbacks will happen at least
+    // |min_time_between_peaks_ms| apart from each other.
+    uint32_t min_time_between_peaks_ms;
+
+    // When enabled causes a TRACE_COUNTER event to be injected in the trace
+    // for each poll (if tracing is enabled).
+    bool enable_verbose_poll_tracing;
+  };
+
   static MemoryPeakDetector* GetInstance();
 
   // Configures the peak detector, binding the polling tasks on the given
@@ -66,7 +88,7 @@
   // If not, the detector remains in the ENABLED state and will start polling
   // automatically (i.e. without requiring another call to Start()) on the
   // next call to NotifyMemoryDumpProvidersChanged().
-  void Start();
+  void Start(Config);
 
   // Stops the polling on the task runner (if was active at all). This doesn't
   // wait for the task runner to drain pending tasks, so it is possible that
@@ -75,24 +97,34 @@
   // with the task runner.
   void Stop();
 
+  // If Start()-ed, prevents that a peak callback is triggered before the next
+  // |min_time_between_peaks_ms|. No-op if the peak detector is not enabled.
+  void Throttle();
+
   // Used by MemoryDumpManager to notify that the list of polling-capable dump
   // providers has changed. The peak detector will reload the list on the next
   // polling task. This function can be called before Setup(), in which
   // case will be just a no-op.
   void NotifyMemoryDumpProvidersChanged();
 
+  void SetStaticThresholdForTesting(uint64_t static_threshold_bytes);
+
  private:
   friend class MemoryPeakDetectorTest;
 
+  static constexpr uint32_t kSlidingWindowNumSamples = 50;
+
   MemoryPeakDetector();
   ~MemoryPeakDetector();
 
   // All these methods are always called on the |task_runner_|.
-  void StartInternal();
+  void StartInternal(Config);
   void StopInternal();
   void TearDownInternal();
   void ReloadDumpProvidersAndStartPollingIfNeeded();
   void PollMemoryAndDetectPeak(uint32_t expected_generation);
+  bool DetectPeakUsingSlidingWindowStddev(uint64_t last_sample_bytes);
+  void ResetPollHistory(bool keep_last_sample = false);
 
   // It is safe to call these testing methods only on the |task_runner_|.
   State state_for_testing() const { return state_; }
@@ -127,7 +159,15 @@
   uint32_t generation_;
 
   State state_;
-  uint32_t polling_interval_ms_;
+
+  // Config passed to Start(), only valid when |state_| = {ENABLED, RUNNING}.
+  Config config_;
+
+  uint64_t static_threshold_bytes_;
+  uint32_t skip_polls_;
+  uint64_t last_dump_memory_total_;
+  uint64_t samples_bytes_[kSlidingWindowNumSamples];
+  uint32_t samples_index_;
   uint32_t poll_tasks_count_for_testing_;
 
   DISALLOW_COPY_AND_ASSIGN(MemoryPeakDetector);
diff --git a/base/trace_event/memory_peak_detector_unittest.cc b/base/trace_event/memory_peak_detector_unittest.cc
index 9a9b922..31342de 100644
--- a/base/trace_event/memory_peak_detector_unittest.cc
+++ b/base/trace_event/memory_peak_detector_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -28,6 +27,12 @@
 
 namespace {
 
+const TimeDelta kMs = TimeDelta::FromMilliseconds(1);
+const MemoryPeakDetector::Config kConfigNoCallbacks = {
+    1 /* polling_interval_ms */, 60000 /* min_time_between_peaks_ms */,
+    false /* enable_verbose_poll_tracing */
+};
+
 class MockMemoryDumpProvider : public MemoryDumpProvider {
  public:
   bool OnMemoryDump(const MemoryDumpArgs&, ProcessMemoryDump*) override {
@@ -52,6 +57,8 @@
   };
 
   MemoryPeakDetectorTest() : testing::Test() {}
+  static const uint64_t kSlidingWindowNumSamples =
+      MemoryPeakDetector::kSlidingWindowNumSamples;
 
   std::unique_ptr<MemoryPeakDetector, FriendDeleter> NewInstance() {
     return std::unique_ptr<MemoryPeakDetector, FriendDeleter>(
@@ -117,6 +124,41 @@
     return res;
   }
 
+  // Runs the peak detector with a mock MDP with the given
+  // |config|. The mock MDP will invoke the |poll_function| on any call to
+  // PollFastMemoryTotal(), until |num_samples| have been polled.
+  // It returns the number of peaks detected.
+  uint32_t RunWithCustomPollFunction(
+      MemoryPeakDetector::Config config,
+      uint32_t num_samples,
+      RepeatingCallback<uint64_t(uint32_t)> poll_function) {
+    WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+    scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider();
+    dump_providers_.push_back(mdp);
+    uint32_t cur_sample_idx = 0;
+    EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_))
+        .WillRepeatedly(Invoke(
+            [&cur_sample_idx, &evt, poll_function, num_samples](uint64_t* mem) {
+              if (cur_sample_idx >= num_samples) {
+                *mem = 1;
+                evt.Signal();
+              } else {
+                *mem = poll_function.Run(cur_sample_idx++);
+              }
+            }));
+
+    uint32_t num_peaks = 0;
+    EXPECT_CALL(on_peak_callback_, OnPeak())
+        .WillRepeatedly(Invoke([&num_peaks] { num_peaks++; }));
+    peak_detector_->Start(config);
+    evt.Wait();  // Wait for |num_samples| invocations of PollFastMemoryTotal().
+    peak_detector_->Stop();
+    EXPECT_EQ(num_samples, cur_sample_idx);
+    EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState());
+    return num_peaks;
+  }
+
   // Called on the |bg_thread_|.
   void MockGetDumpProviders(MemoryPeakDetector::DumpProvidersList* mdps) {
     get_mdp_call_count_++;
@@ -145,6 +187,27 @@
     return *static_cast<MockMemoryDumpProvider*>(mdp_info->dump_provider);
   }
 
+  static uint64_t PollFunctionThatCausesPeakViaStdDev(uint32_t sample_idx) {
+    // Start with a baseline of 50 MB.
+    if (sample_idx < kSlidingWindowNumSamples)
+      return 50000 + (sample_idx % 3) * 100;
+
+    // Then 10 samples around 80 MB
+    if (sample_idx < 10 + kSlidingWindowNumSamples)
+      return 80000 + (sample_idx % 3) * 200;
+
+    // Than back to 60 MB.
+    if (sample_idx < 2 * kSlidingWindowNumSamples)
+      return 60000 + (sample_idx % 3) * 100;
+
+    // Then 20 samples around 120 MB.
+    if (sample_idx < 20 + 2 * kSlidingWindowNumSamples)
+      return 120000 + (sample_idx % 3) * 200;
+
+    // Then back to idle to around 50 MB until the end.
+    return 50000 + (sample_idx % 3) * 100;
+  }
+
  protected:
   MemoryPeakDetector::DumpProvidersList dump_providers_;
   uint32_t get_mdp_call_count_;
@@ -153,9 +216,11 @@
   OnPeakDetectedWrapper on_peak_callback_;
 };
 
+const uint64_t MemoryPeakDetectorTest::kSlidingWindowNumSamples;
+
 TEST_F(MemoryPeakDetectorTest, GetDumpProvidersFunctionCalled) {
   EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState());
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(1u, GetNumGetDumpProvidersCalls());
   EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState());
 
@@ -164,7 +229,7 @@
   EXPECT_EQ(0u, GetNumPollingTasksRan());
 }
 
-TEST_F(MemoryPeakDetectorTest, NotifyBeforeInitialize) {
+TEST_F(MemoryPeakDetectorTest, ThrottleAndNotifyBeforeInitialize) {
   peak_detector_->TearDown();
 
   WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
@@ -173,11 +238,12 @@
   EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_))
       .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); }));
   dump_providers_.push_back(mdp);
+  peak_detector_->Throttle();
   peak_detector_->NotifyMemoryDumpProvidersChanged();
   EXPECT_EQ(MemoryPeakDetector::NOT_INITIALIZED, GetPeakDetectorState());
   RestartThreadAndReinitializePeakDetector();
 
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState());
   evt.Wait();  // Wait for a PollFastMemoryTotal() call.
 
@@ -188,7 +254,7 @@
 }
 
 TEST_F(MemoryPeakDetectorTest, DoubleStop) {
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState());
 
   peak_detector_->Stop();
@@ -209,7 +275,7 @@
       .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); }));
   dump_providers_.push_back(mdp);
 
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   evt.Wait();  // Signaled when PollFastMemoryTotal() is called on the MockMDP.
   EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState());
 
@@ -229,7 +295,7 @@
 
   for (int i = 0; i < 5; ++i) {
     evt.Reset();
-    peak_detector_->Start();
+    peak_detector_->Start(kConfigNoCallbacks);
     evt.Wait();  // Wait for a PollFastMemoryTotal() call.
     // Check that calling TearDown implicitly does a Stop().
     peak_detector_->TearDown();
@@ -240,16 +306,16 @@
 }
 
 TEST_F(MemoryPeakDetectorTest, OneDumpProviderRegisteredOutOfBand) {
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState());
   EXPECT_EQ(1u, GetNumGetDumpProvidersCalls());
 
   // Check that no poll tasks are posted before any dump provider is registered.
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  PlatformThread::Sleep(5 * kConfigNoCallbacks.polling_interval_ms * kMs);
   EXPECT_EQ(0u, GetNumPollingTasksRan());
 
   // Registed the MDP After Start() has been issued and expect that the
-  // PeakDetector transitions ENABLED -> RUNNING on the next
+  // PeakDetector transitions ENABLED -> RUNNING on the next
   // NotifyMemoryDumpProvidersChanged() call.
   WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
                     WaitableEvent::InitialState::NOT_SIGNALED);
@@ -273,7 +339,7 @@
   EXPECT_GT(num_poll_tasks, 0u);
 
   // At this point, no more polling tasks should be posted.
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  PlatformThread::Sleep(5 * kConfigNoCallbacks.polling_interval_ms * kMs);
   peak_detector_->Stop();
   EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState());
   EXPECT_EQ(num_poll_tasks, GetNumPollingTasksRan());
@@ -296,17 +362,15 @@
 
   const TimeTicks tstart = TimeTicks::Now();
   for (int i = 0; i < 5; i++) {
-    peak_detector_->Start();
+    peak_detector_->Start(kConfigNoCallbacks);
     peak_detector_->Stop();
   }
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState());
   evt.Wait();  // Wait for kNumPolls.
   const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
 
-  // TODO(primiano): this will become config.polling_interval_ms in the next CL.
-  const uint32_t polling_interval_ms = 1;
-  EXPECT_GE(time_ms, kNumPolls * polling_interval_ms);
+  EXPECT_GE(time_ms, kNumPolls * kConfigNoCallbacks.polling_interval_ms);
   peak_detector_->Stop();
 }
 
@@ -324,7 +388,7 @@
 
   // Register only one MDP and start the detector.
   dump_providers_.push_back(mdp1);
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState());
 
   // Wait for one poll task and then register also the other one.
@@ -366,9 +430,9 @@
   mdp2 = nullptr;
 
   num_poll_tasks = GetNumPollingTasksRan();
-  peak_detector_->Start();
+  peak_detector_->Start(kConfigNoCallbacks);
   EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState());
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  PlatformThread::Sleep(5 * kConfigNoCallbacks.polling_interval_ms * kMs);
 
   peak_detector_->Stop();
   EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState());
@@ -377,5 +441,116 @@
   EXPECT_EQ(6u, GetNumGetDumpProvidersCalls());
 }
 
+// Tests the behavior of the static threshold detector, which is supposed to
+// detect a peak whenever an increase >= threshold is detected.
+TEST_F(MemoryPeakDetectorTest, StaticThreshold) {
+  const uint32_t kNumSamples = 2 * kSlidingWindowNumSamples;
+  constexpr uint32_t kNumSamplesPerStep = 10;
+  constexpr uint64_t kThreshold = 1000000;
+  peak_detector_->SetStaticThresholdForTesting(kThreshold);
+  const MemoryPeakDetector::Config kConfig = {
+      1 /* polling_interval_ms */, 0 /* min_time_between_peaks_ms */,
+      false /* enable_verbose_poll_tracing */
+  };
+
+  // The mocked PollFastMemoryTotal() will return a step function,
+  // e.g. (1, 1, 1, 5, 5, 5, ...) where the steps are 2x threshold, in order to
+  // trigger only the static threshold logic.
+  auto poll_fn = Bind(
+      [](const uint32_t kNumSamplesPerStep, const uint64_t kThreshold,
+         uint32_t sample_idx) -> uint64_t {
+        return (1 + sample_idx / kNumSamplesPerStep) * 2 * kThreshold;
+      },
+      kNumSamplesPerStep, kThreshold);
+  uint32_t num_peaks = RunWithCustomPollFunction(kConfig, kNumSamples, poll_fn);
+  EXPECT_EQ(kNumSamples / kNumSamplesPerStep - 1, num_peaks);
+}
+
+// Checks the throttling logic of Config's |min_time_between_peaks_ms|.
+TEST_F(MemoryPeakDetectorTest, PeakCallbackThrottling) {
+  const size_t kNumSamples = 2 * kSlidingWindowNumSamples;
+  constexpr uint64_t kThreshold = 1000000;
+  peak_detector_->SetStaticThresholdForTesting(kThreshold);
+  const MemoryPeakDetector::Config kConfig = {
+      1 /* polling_interval_ms */, 4 /* min_time_between_peaks_ms */,
+      false /* enable_verbose_poll_tracing */
+  };
+
+  // Each mock value returned is N * 2 * threshold, so all of them would be
+  // eligible to be a peak if throttling wasn't enabled.
+  auto poll_fn = Bind(
+      [](uint64_t kThreshold, uint32_t sample_idx) -> uint64_t {
+        return (sample_idx + 1) * 2 * kThreshold;
+      },
+      kThreshold);
+  uint32_t num_peaks = RunWithCustomPollFunction(kConfig, kNumSamples, poll_fn);
+  const uint32_t kExpectedThrottlingRate =
+      kConfig.min_time_between_peaks_ms / kConfig.polling_interval_ms;
+  EXPECT_LT(num_peaks, kNumSamples / kExpectedThrottlingRate);
+}
+
+TEST_F(MemoryPeakDetectorTest, StdDev) {
+  // Set the threshold to some arbitrarily high value, so that the static
+  // threshold logic is not hit in this test.
+  constexpr uint64_t kThreshold = 1024 * 1024 * 1024;
+  peak_detector_->SetStaticThresholdForTesting(kThreshold);
+  const size_t kNumSamples = 3 * kSlidingWindowNumSamples;
+  const MemoryPeakDetector::Config kConfig = {
+      1 /* polling_interval_ms */, 0 /* min_time_between_peaks_ms */,
+      false /* enable_verbose_poll_tracing */
+  };
+
+  auto poll_fn = Bind(&PollFunctionThatCausesPeakViaStdDev);
+  uint32_t num_peaks = RunWithCustomPollFunction(kConfig, kNumSamples, poll_fn);
+  EXPECT_EQ(2u, num_peaks);  // 80 MB, 120 MB.
+}
+
+// Tests that Throttle() actually holds back peak notifications.
+TEST_F(MemoryPeakDetectorTest, Throttle) {
+  constexpr uint64_t kThreshold = 1024 * 1024 * 1024;
+  const uint32_t kNumSamples = 3 * kSlidingWindowNumSamples;
+  peak_detector_->SetStaticThresholdForTesting(kThreshold);
+  const MemoryPeakDetector::Config kConfig = {
+      1 /* polling_interval_ms */, 0 /* min_time_between_peaks_ms */,
+      false /* enable_verbose_poll_tracing */
+  };
+
+  auto poll_fn = Bind(
+      [](MemoryPeakDetector* peak_detector, uint32_t sample_idx) -> uint64_t {
+        if (sample_idx % 20 == 20 - 1)
+          peak_detector->Throttle();
+        return PollFunctionThatCausesPeakViaStdDev(sample_idx);
+      },
+      Unretained(&*peak_detector_));
+  uint32_t num_peaks = RunWithCustomPollFunction(kConfig, kNumSamples, poll_fn);
+  EXPECT_EQ(0u, num_peaks);
+}
+
+// Tests that the windows stddev state is not carried over through
+// Stop() -> Start() sequences.
+TEST_F(MemoryPeakDetectorTest, RestartClearsState) {
+  constexpr uint64_t kThreshold = 1024 * 1024 * 1024;
+  peak_detector_->SetStaticThresholdForTesting(kThreshold);
+  const size_t kNumSamples = 3 * kSlidingWindowNumSamples;
+  const MemoryPeakDetector::Config kConfig = {
+      1 /* polling_interval_ms */, 0 /* min_time_between_peaks_ms */,
+      false /* enable_verbose_poll_tracing */
+  };
+  auto poll_fn = Bind(
+      [](MemoryPeakDetector* peak_detector,
+         const uint32_t kSlidingWindowNumSamples,
+         MemoryPeakDetector::Config kConfig, uint32_t sample_idx) -> uint64_t {
+        if (sample_idx % kSlidingWindowNumSamples ==
+            kSlidingWindowNumSamples - 1) {
+          peak_detector->Stop();
+          peak_detector->Start(kConfig);
+        }
+        return PollFunctionThatCausesPeakViaStdDev(sample_idx);
+      },
+      Unretained(&*peak_detector_), kSlidingWindowNumSamples, kConfig);
+  uint32_t num_peaks = RunWithCustomPollFunction(kConfig, kNumSamples, poll_fn);
+  EXPECT_EQ(0u, num_peaks);
+}
+
 }  // namespace trace_event
 }  // namespace base
diff --git a/base/trace_event/trace_category_unittest.cc b/base/trace_event/trace_category_unittest.cc
index a33924f4..25f37ca7 100644
--- a/base/trace_event/trace_category_unittest.cc
+++ b/base/trace_event/trace_category_unittest.cc
@@ -130,7 +130,7 @@
                            WaitableEvent::InitialState::NOT_SIGNALED);
   for (int i = 0; i < kNumThreads; i++) {
     threads[i]->task_runner()->PostTask(
-        FROM_HERE, Bind(&TestRaceThreadMain, Unretained(&sync_event)));
+        FROM_HERE, BindOnce(&TestRaceThreadMain, Unretained(&sync_event)));
   }
   sync_event.Signal();
   for (int i = 0; i < kNumThreads; i++)
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
index db702b6..646b1f2 100644
--- a/base/trace_event/trace_event_argument.cc
+++ b/base/trace_event/trace_event_argument.cc
@@ -289,7 +289,7 @@
       value.GetAsList(&list_value);
       BeginArrayWithCopiedName(name);
       for (const auto& base_value : *list_value)
-        AppendBaseValue(*base_value);
+        AppendBaseValue(base_value);
       EndArray();
     } break;
   }
@@ -343,7 +343,7 @@
       value.GetAsList(&list_value);
       BeginArray();
       for (const auto& base_value : *list_value)
-        AppendBaseValue(*base_value);
+        AppendBaseValue(base_value);
       EndArray();
     } break;
   }
@@ -369,9 +369,11 @@
           cur_dict = new_dict;
         } else {
           cur_list->Append(WrapUnique(new_dict));
+          // |new_dict| is invalidated at this point, so |cur_dict| needs to be
+          // reset.
+          cur_list->GetDictionary(cur_list->GetSize() - 1, &cur_dict);
           stack.push_back(cur_list);
           cur_list = nullptr;
-          cur_dict = new_dict;
         }
       } break;
 
@@ -396,7 +398,8 @@
         } else {
           cur_list->Append(WrapUnique(new_list));
           stack.push_back(cur_list);
-          cur_list = new_list;
+          // |cur_list| is invalidated at this point, so it needs to be reset.
+          cur_list->GetList(cur_list->GetSize() - 1, &cur_list);
         }
       } break;
 
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
index ffc4c08..99f0240a 100644
--- a/base/trace_event/trace_event_memory_overhead.cc
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -105,7 +105,7 @@
       value.GetAsList(&list_value);
       Add("ListValue", sizeof(ListValue));
       for (const auto& v : *list_value)
-        AddValue(*v);
+        AddValue(v);
     } break;
 
     default:
diff --git a/base/trace_event/trace_event_system_stats_monitor.cc b/base/trace_event/trace_event_system_stats_monitor.cc
index 03ef5a6..52e1cdc 100644
--- a/base/trace_event/trace_event_system_stats_monitor.cc
+++ b/base/trace_event/trace_event_system_stats_monitor.cc
@@ -79,16 +79,14 @@
   if (!enabled)
     return;
   task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&TraceEventSystemStatsMonitor::StartProfiling,
-                 weak_factory_.GetWeakPtr()));
+      FROM_HERE, base::BindOnce(&TraceEventSystemStatsMonitor::StartProfiling,
+                                weak_factory_.GetWeakPtr()));
 }
 
 void TraceEventSystemStatsMonitor::OnTraceLogDisabled() {
   task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&TraceEventSystemStatsMonitor::StopProfiling,
-                 weak_factory_.GetWeakPtr()));
+      FROM_HERE, base::BindOnce(&TraceEventSystemStatsMonitor::StopProfiling,
+                                weak_factory_.GetWeakPtr()));
 }
 
 void TraceEventSystemStatsMonitor::StartProfiling() {
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 7a30e4e..5b68c4ef 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -124,8 +124,9 @@
     Thread flush_thread("flush");
     flush_thread.Start();
     flush_thread.task_runner()->PostTask(
-        FROM_HERE, base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync,
-                              base::Unretained(this), &flush_complete_event));
+        FROM_HERE,
+        base::BindOnce(&TraceEventTestFixture::EndTraceAndFlushAsync,
+                       base::Unretained(this), &flush_complete_event));
     flush_complete_event.Wait();
   }
 
@@ -1737,7 +1738,8 @@
   thread.Start();
 
   thread.task_runner()->PostTask(
-      FROM_HERE, base::Bind(&TraceWithAllMacroVariants, &task_complete_event));
+      FROM_HERE,
+      base::BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
   task_complete_event.Wait();
   thread.Stop();
 
@@ -1760,8 +1762,8 @@
                           WaitableEvent::InitialState::NOT_SIGNALED);
     threads[i]->Start();
     threads[i]->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&TraceManyInstantEvents, i, num_events,
-                              task_complete_events[i]));
+        FROM_HERE, base::BindOnce(&TraceManyInstantEvents, i, num_events,
+                                  task_complete_events[i]));
   }
 
   for (int i = 0; i < num_threads; i++) {
@@ -1810,8 +1812,8 @@
     threads[i]->Start();
     thread_ids[i] = threads[i]->GetThreadId();
     threads[i]->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&TraceManyInstantEvents, i, kNumEvents,
-                              task_complete_events[i]));
+        FROM_HERE, base::BindOnce(&TraceManyInstantEvents, i, kNumEvents,
+                                  task_complete_events[i]));
   }
   for (int i = 0; i < kNumThreads; i++) {
     task_complete_events[i]->Wait();
@@ -2720,11 +2722,11 @@
                                     WaitableEvent::InitialState::NOT_SIGNALED);
   thread.Start();
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&TraceLog::SetCurrentThreadBlocksMessageLoop,
-                      Unretained(TraceLog::GetInstance())));
+      FROM_HERE, BindOnce(&TraceLog::SetCurrentThreadBlocksMessageLoop,
+                          Unretained(TraceLog::GetInstance())));
 
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
   task_complete_event.Wait();
 
   WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC,
@@ -2732,7 +2734,8 @@
   WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
                                 WaitableEvent::InitialState::NOT_SIGNALED);
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
   task_start_event.Wait();
 
   EndTraceAndFlush();
@@ -2776,16 +2779,16 @@
   thread.Start();
 
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
   task_complete_event.Wait();
 
   WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC,
                                  WaitableEvent::InitialState::NOT_SIGNALED);
   WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
                                 WaitableEvent::InitialState::NOT_SIGNALED);
-  thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&SetBlockingFlagAndBlockUntilStopped, &task_start_event,
-                      &task_stop_event));
+  thread.task_runner()->PostTask(FROM_HERE,
+                                 BindOnce(&SetBlockingFlagAndBlockUntilStopped,
+                                          &task_start_event, &task_stop_event));
   task_start_event.Wait();
 
   EndTraceAndFlush();
@@ -2804,7 +2807,7 @@
   thread.Start();
 
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
   task_complete_event.Wait();
   task_complete_event.Reset();
 
@@ -2813,7 +2816,8 @@
   WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
                                 WaitableEvent::InitialState::NOT_SIGNALED);
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
   task_start_event.Wait();
 
   // The thread will timeout in this flush.
@@ -2828,7 +2832,8 @@
   task_start_event.Reset();
   task_stop_event.Reset();
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
   task_start_event.Wait();
   task_stop_event.Signal();
   Clear();
@@ -2837,7 +2842,7 @@
   // local buffer for the thread without any error.
   BeginTrace();
   thread.task_runner()->PostTask(
-      FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
   task_complete_event.Wait();
   task_complete_event.Reset();
   EndTraceAndFlushInThreadWithMessageLoop();
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 07d124c..3e9375d 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -647,8 +647,8 @@
     observer->OnTraceLogEnabled();
   for (const auto& it : observer_map) {
     it.second.task_runner->PostTask(
-        FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogEnabled,
-                        it.second.observer));
+        FROM_HERE, BindOnce(&AsyncEnabledStateObserver::OnTraceLogEnabled,
+                            it.second.observer));
   }
 
   {
@@ -748,8 +748,8 @@
       observer->OnTraceLogDisabled();
     for (const auto& it : observer_map) {
       it.second.task_runner->PostTask(
-          FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogDisabled,
-                          it.second.observer));
+          FROM_HERE, BindOnce(&AsyncEnabledStateObserver::OnTraceLogDisabled,
+                              it.second.observer));
     }
   }
   dispatching_to_observer_list_ = false;
@@ -893,12 +893,13 @@
   if (!thread_message_loop_task_runners.empty()) {
     for (auto& task_runner : thread_message_loop_task_runners) {
       task_runner->PostTask(
-          FROM_HERE, Bind(&TraceLog::FlushCurrentThread, Unretained(this),
-                          gen, discard_events));
+          FROM_HERE, BindOnce(&TraceLog::FlushCurrentThread, Unretained(this),
+                              gen, discard_events));
     }
     flush_task_runner_->PostDelayedTask(
-        FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), gen,
-                        discard_events),
+        FROM_HERE,
+        BindOnce(&TraceLog::OnFlushTimeout, Unretained(this), gen,
+                 discard_events),
         TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
     return;
   }
@@ -969,14 +970,15 @@
 
   if (use_worker_thread_) {
     base::PostTaskWithTraits(
-        FROM_HERE, base::TaskTraits()
-                       .MayBlock()
-                       .WithPriority(base::TaskPriority::BACKGROUND)
-                       .WithShutdownBehavior(
-                           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
-        Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
-             Passed(&previous_logged_events), flush_output_callback,
-             argument_filter_predicate));
+        FROM_HERE,
+        base::TaskTraits()
+            .MayBlock()
+            .WithPriority(base::TaskPriority::BACKGROUND)
+            .WithShutdownBehavior(
+                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+        BindOnce(&TraceLog::ConvertTraceEventsToTraceFormat,
+                 Passed(&previous_logged_events), flush_output_callback,
+                 argument_filter_predicate));
     return;
   }
 
@@ -1004,8 +1006,8 @@
     return;
 
   flush_task_runner_->PostTask(
-      FROM_HERE, Bind(&TraceLog::FinishFlush, Unretained(this), generation,
-                      discard_events));
+      FROM_HERE, BindOnce(&TraceLog::FinishFlush, Unretained(this), generation,
+                          discard_events));
 }
 
 void TraceLog::OnFlushTimeout(int generation, bool discard_events) {
diff --git a/base/values.cc b/base/values.cc
index ca12edc..74e83b9 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -35,7 +35,7 @@
 std::unique_ptr<ListValue> CopyListWithoutEmptyChildren(const ListValue& list) {
   std::unique_ptr<ListValue> copy;
   for (const auto& entry : list) {
-    std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(*entry);
+    std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry);
     if (child_copy) {
       if (!copy)
         copy.reset(new ListValue);
@@ -375,12 +375,7 @@
                                  std::tie(v.first, *v.second);
                         });
     case Value::Type::LIST:
-      if (lhs.list_->size() != rhs.list_->size())
-        return false;
-      return std::equal(
-          std::begin(*lhs.list_), std::end(*lhs.list_), std::begin(*rhs.list_),
-          [](const Value::ListStorage::value_type& u,
-             const Value::ListStorage::value_type& v) { return *u == *v; });
+      return *lhs.list_ == *rhs.list_;
   }
 
   NOTREACHED();
@@ -419,11 +414,7 @@
             return std::tie(u.first, *u.second) < std::tie(v.first, *v.second);
           });
     case Value::Type::LIST:
-      return std::lexicographical_compare(
-          std::begin(*lhs.list_), std::end(*lhs.list_), std::begin(*rhs.list_),
-          std::end(*rhs.list_),
-          [](const Value::ListStorage::value_type& u,
-             const Value::ListStorage::value_type& v) { return *u < *v; });
+      return *lhs.list_ < *rhs.list_;
   }
 
   NOTREACHED();
@@ -494,11 +485,10 @@
     case Type::BINARY:
       binary_value_.Init(*that.binary_value_);
       return;
-    // DictStorage and ListStorage are move-only types due to the presence of
-    // unique_ptrs. This is why the explicit copy of every element is necessary
-    // here.
-    // TODO(crbug.com/646113): Clean this up when DictStorage and ListStorage
-    // can be copied directly.
+    // DictStorage is a move-only type due to the presence of unique_ptrs. This
+    // is why the explicit copy of every element is necessary here.
+    // TODO(crbug.com/646113): Clean this up when DictStorage can be copied
+    // directly.
     case Type::DICTIONARY:
       dict_ptr_.Init(MakeUnique<DictStorage>());
       for (const auto& it : **that.dict_ptr_) {
@@ -508,10 +498,7 @@
       }
       return;
     case Type::LIST:
-      list_.Init();
-      list_->reserve(that.list_->size());
-      for (const auto& it : *that.list_)
-        list_->push_back(MakeUnique<Value>(*it));
+      list_.Init(*that.list_);
       return;
   }
 }
@@ -561,16 +548,17 @@
     case Type::BINARY:
       *binary_value_ = *that.binary_value_;
       return;
-    // DictStorage and ListStorage are move-only types due to the presence of
-    // unique_ptrs. This is why the explicit call to the copy constructor is
-    // necessary here.
-    // TODO(crbug.com/646113): Clean this up when DictStorage and ListStorage
-    // can be copied directly.
-    case Type::DICTIONARY:
-      *dict_ptr_ = std::move(*Value(that).dict_ptr_);
+    // DictStorage is a move-only type due to the presence of unique_ptrs. This
+    // is why the explicit call to the copy constructor is necessary here.
+    // TODO(crbug.com/646113): Clean this up when DictStorage can be copied
+    // directly.
+    case Type::DICTIONARY: {
+      Value copy = that;
+      *dict_ptr_ = std::move(*copy.dict_ptr_);
       return;
+    }
     case Type::LIST:
-      *list_ = std::move(*Value(that).list_);
+      *list_ = *that.list_;
       return;
   }
 }
@@ -1077,6 +1065,10 @@
   list_->clear();
 }
 
+void ListValue::Reserve(size_t n) {
+  list_->reserve(n);
+}
+
 bool ListValue::Set(size_t index, Value* in_value) {
   return Set(index, WrapUnique(in_value));
 }
@@ -1091,9 +1083,7 @@
       Append(MakeUnique<Value>());
     Append(std::move(in_value));
   } else {
-    // TODO(dcheng): remove this DCHECK once the raw pointer version is removed?
-    DCHECK((*list_)[index] != in_value);
-    (*list_)[index] = std::move(in_value);
+    (*list_)[index] = std::move(*in_value);
   }
   return true;
 }
@@ -1103,7 +1093,7 @@
     return false;
 
   if (out_value)
-    *out_value = (*list_)[index].get();
+    *out_value = &(*list_)[index];
 
   return true;
 }
@@ -1213,36 +1203,35 @@
     return false;
 
   if (out_value)
-    *out_value = std::move((*list_)[index]);
+    *out_value = MakeUnique<Value>(std::move((*list_)[index]));
 
   list_->erase(list_->begin() + index);
   return true;
 }
 
 bool ListValue::Remove(const Value& value, size_t* index) {
-  for (auto it = list_->begin(); it != list_->end(); ++it) {
-    if (**it == value) {
-      size_t previous_index = it - list_->begin();
-      list_->erase(it);
+  auto it = std::find(list_->begin(), list_->end(), value);
 
-      if (index)
-        *index = previous_index;
-      return true;
-    }
-  }
-  return false;
+  if (it == list_->end())
+    return false;
+
+  if (index)
+    *index = std::distance(list_->begin(), it);
+
+  list_->erase(it);
+  return true;
 }
 
 ListValue::iterator ListValue::Erase(iterator iter,
                                      std::unique_ptr<Value>* out_value) {
   if (out_value)
-    *out_value = std::move(*ListStorage::iterator(iter));
+    *out_value = MakeUnique<Value>(std::move(*iter));
 
   return list_->erase(iter);
 }
 
 void ListValue::Append(std::unique_ptr<Value> in_value) {
-  list_->push_back(std::move(in_value));
+  list_->push_back(std::move(*in_value));
 }
 
 #if !defined(OS_LINUX)
@@ -1288,11 +1277,10 @@
 
 bool ListValue::AppendIfNotPresent(std::unique_ptr<Value> in_value) {
   DCHECK(in_value);
-  for (const auto& entry : *list_) {
-    if (*entry == *in_value)
-      return false;
-  }
-  list_->push_back(std::move(in_value));
+  if (std::find(list_->begin(), list_->end(), *in_value) != list_->end())
+    return false;
+
+  list_->push_back(std::move(*in_value));
   return true;
 }
 
@@ -1301,15 +1289,12 @@
   if (index > list_->size())
     return false;
 
-  list_->insert(list_->begin() + index, std::move(in_value));
+  list_->insert(list_->begin() + index, std::move(*in_value));
   return true;
 }
 
 ListValue::const_iterator ListValue::Find(const Value& value) const {
-  return std::find_if(list_->begin(), list_->end(),
-                      [&value](const std::unique_ptr<Value>& entry) {
-                        return *entry == value;
-                      });
+  return std::find(list_->begin(), list_->end(), value);
 }
 
 void ListValue::Swap(ListValue* other) {
diff --git a/base/values.h b/base/values.h
index ace8b436..075bc9f 100644
--- a/base/values.h
+++ b/base/values.h
@@ -49,7 +49,7 @@
 class BASE_EXPORT Value {
  public:
   using DictStorage = base::flat_map<std::string, std::unique_ptr<Value>>;
-  using ListStorage = std::vector<std::unique_ptr<Value>>;
+  using ListStorage = std::vector<Value>;
 
   enum class Type {
     NONE = 0,
@@ -390,9 +390,15 @@
   // Returns the number of Values in this list.
   size_t GetSize() const { return list_->size(); }
 
+  // Returns the capacity of storage for Values in this list.
+  size_t capacity() const { return list_->capacity(); }
+
   // Returns whether the list is empty.
   bool empty() const { return list_->empty(); }
 
+  // Reserves storage for at least |n| values.
+  void Reserve(size_t n);
+
   // Sets the list item at the given index to be the Value specified by
   // the value given.  If the index beyond the current end of the list, null
   // Values will be used to pad out the list.
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 077f54c..ae9165806 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -437,7 +437,7 @@
   base::Value not_found_value(false);
 
   ASSERT_NE(mixed_list->end(), mixed_list->Find(sought_value));
-  ASSERT_TRUE((*mixed_list->Find(sought_value))->GetAsInteger(&int_value));
+  ASSERT_TRUE((*mixed_list->Find(sought_value)).GetAsInteger(&int_value));
   ASSERT_EQ(42, int_value);
   ASSERT_EQ(mixed_list->end(), mixed_list->Find(not_found_value));
 }
@@ -540,10 +540,10 @@
   {
     ListValue list;
     auto value = MakeUnique<Value>();
-    Value* original_value = value.get();
+    Value original_value = *value;
     list.Append(std::move(value));
     size_t index = 0;
-    list.Remove(*original_value, &index);
+    list.Remove(original_value, &index);
     EXPECT_EQ(0U, index);
     EXPECT_EQ(0U, list.GetSize());
   }
diff --git a/cc/base/completion_event.h b/cc/base/completion_event.h
index edee0b7..0c620db 100644
--- a/cc/base/completion_event.h
+++ b/cc/base/completion_event.h
@@ -43,12 +43,18 @@
     event_.Wait();
   }
 
-  void TimedWait(const base::TimeDelta& max_time) {
+  bool TimedWait(const base::TimeDelta& max_time) {
 #if DCHECK_IS_ON()
     DCHECK(!waited_);
     waited_ = true;
 #endif
-    event_.TimedWait(max_time);
+    base::ThreadRestrictions::ScopedAllowWait allow_wait;
+    if (event_.TimedWait(max_time))
+      return true;
+#if DCHECK_IS_ON()
+    waited_ = false;
+#endif
+    return false;
   }
 
   bool IsSignaled() { return event_.IsSignaled(); }
diff --git a/cc/base/contiguous_container_unittest.cc b/cc/base/contiguous_container_unittest.cc
index dd44665..9e5e87b0 100644
--- a/cc/base/contiguous_container_unittest.cc
+++ b/cc/base/contiguous_container_unittest.cc
@@ -161,16 +161,6 @@
                 "Non-const iteration should produce non-const references.");
 }
 
-// Checks that the latter list has pointers to the elements of the former.
-template <typename It1, typename It2>
-bool EqualPointers(It1 it1, const It1& end1, It2 it2) {
-  for (; it1 != end1; ++it1, ++it2) {
-    if (&*it1 != *it2)
-      return false;
-  }
-  return true;
-}
-
 TEST(ContiguousContainerTest, CapacityInBytes) {
   const int iterations = 500;
   const size_t initial_capacity = 10 * kMaxPointSize;
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index 11645168..d0738a0 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -152,7 +152,7 @@
 
   success &= dict->GetList("Children", &list);
   for (const auto& value : *list) {
-    new_layer->AddChild(ParseTreeFromValue(*value, content_client));
+    new_layer->AddChild(ParseTreeFromValue(value, content_client));
   }
 
   if (!success)
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 39040a4..197ac4fe 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -4,10 +4,14 @@
 
 #include "cc/trees/proxy_impl.h"
 
+#include <string.h>
+
 #include <algorithm>
 #include <string>
 
 #include "base/auto_reset.h"
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
@@ -223,6 +227,28 @@
   completion->Signal();
 }
 
+// TODO(sunnyps): Remove this code once crbug.com/668892 is fixed.
+NOINLINE void ProxyImpl::DumpForBeginMainFrameHang() {
+  DCHECK(IsImplThread());
+  DCHECK(scheduler_);
+
+  char stack_string[20000] = "";
+  base::debug::Alias(&stack_string);
+
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat> scheduler_state =
+      scheduler_->AsValue();
+  strncat(stack_string, scheduler_state->ToString().c_str(),
+          arraysize(stack_string) - strlen(stack_string) - 1);
+
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+      tile_manager_state =
+          layer_tree_host_impl_->tile_manager()->ActivationStateAsValue();
+  strncat(stack_string, tile_manager_state->ToString().c_str(),
+          arraysize(stack_string) - strlen(stack_string) - 1);
+
+  base::debug::DumpWithoutCrashing();
+}
+
 void ProxyImpl::NotifyReadyToCommitOnImpl(
     CompletionEvent* completion,
     LayerTreeHost* layer_tree_host,
diff --git a/cc/trees/proxy_impl.h b/cc/trees/proxy_impl.h
index a0482ea2..68edc33 100644
--- a/cc/trees/proxy_impl.h
+++ b/cc/trees/proxy_impl.h
@@ -56,6 +56,8 @@
   void MainFrameWillHappenOnImplForTesting(CompletionEvent* completion,
                                            bool* main_frame_will_happen);
 
+  NOINLINE void DumpForBeginMainFrameHang();
+
  private:
   // The members of this struct should be accessed on the impl thread only when
   // the main thread is blocked for a commit.
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index de79051..ceb9441 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -265,7 +265,17 @@
                               base::Unretained(proxy_impl_.get()), &completion,
                               layer_tree_host_, begin_main_frame_start_time,
                               hold_commit_for_activation));
-    completion.Wait();
+    // TODO(sunnyps): Remove this code once crbug.com/668892 is fixed.
+    // content/common/content_constants_internal.cc defines hung renderer delay
+    // as 30s so choose something less than that but still sufficiently long.
+    static constexpr base::TimeDelta kDumpDelay =
+        base::TimeDelta::FromSeconds(20);
+    if (!completion.TimedWait(kDumpDelay)) {
+      ImplThreadTaskRunner()->PostTask(
+          FROM_HERE, base::Bind(&ProxyImpl::DumpForBeginMainFrameHang,
+                                base::Unretained(proxy_impl_.get())));
+      completion.Wait();
+    }
   }
 
   current_pipeline_stage_ = NO_PIPELINE_STAGE;
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 167e290c..a528f07 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -729,7 +729,7 @@
         <receiver android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
             android:exported="false" />
 
-        {% if channel in ['default'] %}
+        {% if channel in ['canary', 'default'] %}
         <!-- Search widget -->
         <receiver android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProvider"
             android:process=":search_widget">
@@ -748,10 +748,10 @@
 
         <!-- Search Activity -->
         <activity android:name="org.chromium.chrome.browser.searchwidget.SearchActivity"
-            android:theme="@style/OverlayTheme"
+            android:theme="@style/MainTheme"
             android:label="Search"
             android:exported="false"
-            android:launchMode="singleInstance"
+            android:launchMode="singleTask"
             android:taskAffinity=""
             android:clearTaskOnLaunch="true"
             android:excludeFromRecents="true"
diff --git a/chrome/android/java/res/drawable/toolbar_handle_dark.xml b/chrome/android/java/res/drawable/toolbar_handle_dark.xml
new file mode 100644
index 0000000..7dd86d8a
--- /dev/null
+++ b/chrome/android/java/res/drawable/toolbar_handle_dark.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid android:color="@color/black_alpha_40" />
+    <size
+        android:width="@dimen/toolbar_handle_width"
+        android:height="@dimen/toolbar_handle_height" />
+    <corners android:radius="@dimen/toolbar_handle_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/toolbar_handle_light.xml b/chrome/android/java/res/drawable/toolbar_handle_light.xml
new file mode 100644
index 0000000..f0de86c
--- /dev/null
+++ b/chrome/android/java/res/drawable/toolbar_handle_light.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid android:color="@color/white_alpha_50" />
+    <size
+        android:width="@dimen/toolbar_handle_width"
+        android:height="@dimen/toolbar_handle_height" />
+    <corners android:radius="@dimen/toolbar_handle_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/button_preference_button.xml b/chrome/android/java/res/layout/button_preference_button.xml
new file mode 100644
index 0000000..47ad44b
--- /dev/null
+++ b/chrome/android/java/res/layout/button_preference_button.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- Layout used by ButtonPreference for the button widget style. -->
+<org.chromium.ui.widget.ButtonCompat
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:chrome="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/button_preference"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:minHeight="40dp"
+    android:textColor="@android:color/white"
+    chrome:buttonColor="@color/pref_accent_color" />
diff --git a/chrome/android/java/res/layout/preference_button.xml b/chrome/android/java/res/layout/button_preference_layout.xml
similarity index 63%
rename from chrome/android/java/res/layout/preference_button.xml
rename to chrome/android/java/res/layout/button_preference_layout.xml
index 5a18078..aac3f17 100644
--- a/chrome/android/java/res/layout/preference_button.xml
+++ b/chrome/android/java/res/layout/button_preference_layout.xml
@@ -4,23 +4,13 @@
      found in the LICENSE file. -->
 
 <!-- Layout used by ButtonPreference for the button widget style. -->
-
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:chrome="http://schemas.android.com/apk/res-auto"
     style="@style/PreferenceLayout"
+    android:id="@android:id/widget_frame"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:clipToPadding="false"
     android:gravity="center_vertical"
-    android:orientation="vertical">
-
-    <org.chromium.ui.widget.ButtonCompat
-        android:id="@+id/button_preference"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:minHeight="40dp"
-        android:textColor="#fff"
-        chrome:buttonColor="@color/pref_accent_color" />
-
-</LinearLayout>
+    android:orientation="vertical" />
diff --git a/chrome/android/java/res/layout/divider_preference.xml b/chrome/android/java/res/layout/divider_preference.xml
index a0407e3..a12b713 100644
--- a/chrome/android/java/res/layout/divider_preference.xml
+++ b/chrome/android/java/res/layout/divider_preference.xml
@@ -3,4 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<View style="@style/Divider"/>
+<View 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/Divider"
+    android:importantForAccessibility="no" />
diff --git a/chrome/android/java/res/layout/search_activity.xml b/chrome/android/java/res/layout/search_activity.xml
index 378e268..739da14 100644
--- a/chrome/android/java/res/layout/search_activity.xml
+++ b/chrome/android/java/res/layout/search_activity.xml
@@ -7,7 +7,7 @@
         android:id="@+id/control_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="#9affffff" >
+        android:background="@color/google_grey_500" >
 
     <!-- This view is unnecessary visually, but required for the LocationBarLayout. -->
     <org.chromium.chrome.browser.searchwidget.SearchActivityFadingBackgroundView
@@ -43,9 +43,9 @@
                 android:id="@+id/search_location_bar"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
                 android:layout_marginStart="@dimen/tablet_toolbar_start_padding_no_buttons"
-                android:layout_marginEnd="@dimen/tablet_toolbar_start_padding_no_buttons"
-                android:layout_marginTop="@dimen/tablet_toolbar_start_padding_no_buttons" />
+                android:layout_marginEnd="@dimen/tablet_toolbar_start_padding_no_buttons" />
 
     </FrameLayout>
 
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 23a2d095..cb67fee 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -73,16 +73,6 @@
         <item name="android:maxLines">2</item>
     </style>
 
-    <style name="OverlayTheme" parent="MainTheme">
-      <!-- Using these values will make the Activity translucent.
-           <item name="android:windowIsTranslucent">true</item>
-           <item name="android:windowBackground">@android:color/transparent</item>
-           <item name="android:windowContentOverlay">@null</item>
-           <item name="android:windowNoTitle">true</item>
-      -->
-      <item name="android:windowBackground">@color/light_normal_color</item>
-    </style>
-
     <style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index e09f050..77d6ea1 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -121,6 +121,8 @@
     <!-- Overlay panel dimensions -->
     <dimen name="overlay_panel_bar_height">56dp</dimen>
     <dimen name="overlay_panel_text_size">18sp</dimen>
+    <dimen name="overlay_panel_bar_handle_offset_y">6dp</dimen>
+    <dimen name="overlay_panel_bar_padding_bottom">4dp</dimen>
 
     <!-- Password generation popup dimensions -->
     <dimen name="password_generation_divider_height">1dp</dimen>
@@ -247,6 +249,7 @@
     <dimen name="toolbar_button_width">48dp</dimen>
     <dimen name="toolbar_handle_height">4dp</dimen>
     <dimen name="toolbar_handle_width">40dp</dimen>
+    <dimen name="toolbar_handle_corner_radius">2dp</dimen>
     <dimen name="toolbar_handle_margin_top">14dp</dimen>
 
     <dimen name="toolbar_edge_padding">8dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index eb333010..43b71b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -90,20 +90,20 @@
      */
     private static final float PROGRESS_BAR_VISIBILITY_THRESHOLD_DP = 10.f;
 
+    /** Ratio of dps per pixel. */
+    protected final float mPxToDp;
+
     /** The height of the Toolbar in dps. */
     private float mToolbarHeight;
 
     /** The height of the Bar when the Panel is peeking, in dps. */
-    private final float mBarHeightPeeking;
+    private float mBarHeightPeeking;
 
     /** The height of the Bar when the Panel is expanded, in dps. */
-    private final float mBarHeightExpanded;
+    private float mBarHeightExpanded;
 
     /** The height of the Bar when the Panel is maximized, in dps. */
-    private final float mBarHeightMaximized;
-
-    /** Ratio of dps per pixel. */
-    protected final float mPxToDp;
+    private float mBarHeightMaximized;
 
     /**
      * The Y coordinate to apply to the Base Page in order to keep the selection
@@ -141,12 +141,11 @@
         mContext = context;
         mPxToDp = 1.f / mContext.getResources().getDisplayMetrics().density;
 
-        mBarHeightPeeking = mContext.getResources().getDimension(
-                R.dimen.overlay_panel_bar_height) * mPxToDp;
-        mBarHeightMaximized = mContext.getResources().getDimension(
-                R.dimen.toolbar_height_no_shadow) * mPxToDp;
-        mBarHeightExpanded =
-                Math.round((mBarHeightPeeking + mBarHeightMaximized) / 2.f);
+        mBarHeightPeeking =
+                mContext.getResources().getDimension(R.dimen.overlay_panel_bar_height) * mPxToDp;
+        mBarHeightMaximized =
+                mContext.getResources().getDimension(R.dimen.toolbar_height_no_shadow) * mPxToDp;
+        mBarHeightExpanded = Math.round((mBarHeightPeeking + mBarHeightMaximized) / 2.f);
 
         mBarMarginSide = BAR_ICON_SIDE_PADDING_DP;
         mProgressBarHeight = PROGRESS_BAR_HEIGHT_DP;
@@ -1140,6 +1139,54 @@
     }
 
     // ============================================================================================
+    // Bar Handle
+    // ============================================================================================
+    protected int mBarHandleResourceId;
+    protected float mBarHandleOffsetY;
+    protected float mBarPaddingBottom;
+
+    /**
+     * Adds a handle to the bar.
+     * @param barHeightPx The new bar height in px needed to accommodate the handle.
+     */
+    public void addBarHandle(int barHeightPx) {
+        mBarHandleResourceId = R.drawable.toolbar_handle_dark;
+        mBarHeightPeeking = barHeightPx * mPxToDp;
+        mBarHeightMaximized = mBarHeightPeeking;
+        mBarHeightExpanded = mBarHeightPeeking;
+        mHeight = mBarHeightPeeking;
+    }
+
+    /**
+     * @return The resource id for the bar handle.
+     */
+    public int getBarHandleResourceId() {
+        return mBarHandleResourceId;
+    }
+
+    /** @return The y-offset for the bar handle in px. */
+    public float getBarHandleOffsetY() {
+        if (mBarHandleOffsetY == 0.f && mBarHandleResourceId != 0) {
+            mBarHandleOffsetY =
+                    mContext.getResources().getDimension(R.dimen.overlay_panel_bar_handle_offset_y);
+        }
+        return mBarHandleOffsetY;
+    }
+
+    /** @return The bottom padding for the bar in px. */
+    public float getBarPaddingBottom() {
+        // When there is no bar handle, the bar contents are vertically centered. When there is a
+        // bar handle, the contents are displayed below the handle. Bottom padding is needed so that
+        // the contents don't appear too low in the bar when centered in the space beneath the
+        // handle.
+        if (mBarPaddingBottom == 0.f && mBarHandleResourceId != 0) {
+            mBarPaddingBottom =
+                    mContext.getResources().getDimension(R.dimen.overlay_panel_bar_padding_bottom);
+        }
+        return mBarPaddingBottom;
+    }
+
+    // ============================================================================================
     // Test Infrastructure
     // ============================================================================================
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index bbacc3a..3d9bbcae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -12,8 +12,8 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.VisibleForTesting;
-
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayContentProgressObserver;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
@@ -406,6 +406,15 @@
                 || super.shouldHideAndroidBrowserControls();
     }
 
+    @Override
+    public void setChromeActivity(ChromeActivity activity) {
+        super.setChromeActivity(activity);
+
+        if (mActivity.getBottomSheet() == null) return;
+
+        addBarHandle(mActivity.getToolbarManager().getToolbar().getHeight());
+    }
+
     // ============================================================================================
     // Animation Handling
     // ============================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index cb26d3b..7f9e197 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -127,6 +127,10 @@
         float touchHighlightXOffset = searchBarControl.getTouchHighlightXOffsetPx();
         float touchHighlightWidth = searchBarControl.getTouchHighlightWidthPx();
 
+        int barHandleResId = panel.getBarHandleResourceId();
+        float barHandleOffsetY = panel.getBarHandleOffsetY();
+        float barPaddingBottom = panel.getBarPaddingBottom();
+
         WebContents panelWebContents = panel.getContentViewCore() != null
                 ? panel.getContentViewCore().getWebContents() : null;
 
@@ -152,7 +156,8 @@
                 progressBarHeight * mDpToPx, progressBarOpacity, progressBarCompletion,
                 dividerLineVisibilityPercentage, dividerLineWidth, dividerLineHeight,
                 dividerLineColor, dividerLineXOffset, touchHighlightVisible, touchHighlightXOffset,
-                touchHighlightWidth, Profile.getLastUsedProfile());
+                touchHighlightWidth, barHandleResId, barHandleOffsetY, barPaddingBottom,
+                Profile.getLastUsedProfile());
     }
 
     @CalledByNative
@@ -223,5 +228,6 @@
             float progressBarHeight, float progressBarOpacity, int progressBarCompletion,
             float dividerLineVisibilityPercentage, float dividerLineWidth, float dividerLineHeight,
             int dividerLineColor, float dividerLineXOffset, boolean touchHighlightVisible,
-            float touchHighlightXOffset, float toucHighlightWidth, Profile profile);
+            float touchHighlightXOffset, float toucHighlightWidth, int barHandleResId,
+            float barHandleOffsetY, float barPaddingBottom, Profile profile);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 095e521f..129aaf58 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -46,6 +46,7 @@
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
@@ -366,11 +367,8 @@
     private static Notification buildSummaryNotificationWithIcon(Context context, int iconId) {
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                context.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                context.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setContentTitle(
                                 context.getString(R.string.download_notification_summary_title))
                         .setSubText(context.getString(R.string.menu_downloads))
@@ -1080,11 +1078,8 @@
 
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                mContext.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                mContext.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setContentTitle(
                                 DownloadUtils.getAbbreviatedFileName(title, MAX_FILE_NAME_LENGTH))
                         .setSmallIcon(iconId)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index c4014ba4..3c28280 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -23,8 +23,8 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
@@ -459,23 +459,15 @@
     public static Uri getUriForItem(File file) {
         Uri uri = null;
 
-        // #getContentUriFromFile causes a disk read when it calls into FileProvider#getUriForFile.
-        // Obtaining a content URI is on the critical path for creating a share intent after the
-        // user taps on the share button, so even if we were to run this method on a background
-        // thread we would have to wait. As it depends on user-selected items, we cannot
-        // know/preload which URIs we need until the user presses share.
+        // FileUtils.getUriForFile() causes a disk read when it calls into
+        // FileProvider#getUriForFile. Obtaining a content URI is on the critical path for creating
+        // a share intent after the user taps on the share button, so even if we were to run this
+        // method on a background thread we would have to wait. As it depends on user-selected
+        // items, we cannot know/preload which URIs we need until the user presses share.
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            // Try to obtain a content:// URI, which is preferred to a file:// URI so that
-            // receiving apps don't attempt to determine the file's mime type (which often fails).
-            uri = ContentUriUtils.getContentUriFromFile(file);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Could not create content uri: " + e);
-        }
+        uri = FileUtils.getUriForFile(file);
         StrictMode.setThreadPolicy(oldPolicy);
 
-        if (uri == null) uri = Uri.fromFile(file);
-
         return uri;
     }
 
@@ -509,7 +501,11 @@
 
         // Check if any apps can open the file.
         try {
-            Intent viewIntent = createViewIntentForDownloadItem(getUriForItem(file), mimeType);
+            // TODO(qinmin): Move this to an AsyncTask so we don't need to temper with strict mode.
+            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            Uri uri = ApiCompatibilityUtils.getUriForDownloadedFile(file);
+            StrictMode.setThreadPolicy(oldPolicy);
+            Intent viewIntent = createViewIntentForDownloadItem(uri, mimeType);
             context.startActivity(viewIntent);
             service.updateLastAccessTime(downloadGuid, isOffTheRecord);
             return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
index a033b03..0b1cd04 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
@@ -10,6 +10,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
@@ -33,11 +34,8 @@
 
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                context.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                context.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setContentTitle(title)
                         .setContentIntent(
                                 IncognitoNotificationService.getRemoveAllIncognitoTabsIntent(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
index 92c5a808..ea732313 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
@@ -41,6 +41,9 @@
     private static final String INSTANT_APP_START_TIME_EXTRA =
             "org.chromium.chrome.INSTANT_APP_START_TIME";
 
+    // TODO(mariakhomenko): Use system once we roll to O SDK.
+    private static final int FLAG_DO_NOT_LAUNCH = 0x00000200;
+
     // TODO(mariakhomenko): Depend directly on the constants once we roll to v8 libraries.
     private static final String DO_NOT_LAUNCH_EXTRA =
             "com.google.android.gms.instantapps.DO_NOT_LAUNCH_INSTANT_APP";
@@ -221,7 +224,8 @@
             return false;
         }
 
-        if (IntentUtils.safeGetBooleanExtra(intent, DO_NOT_LAUNCH_EXTRA, false)) {
+        if (IntentUtils.safeGetBooleanExtra(intent, DO_NOT_LAUNCH_EXTRA, false)
+                || ((intent.getFlags() & FLAG_DO_NOT_LAUNCH) != 0)) {
             maybeRecordFallbackStats(intent);
             Log.i(TAG, "Not handling with Instant Apps (DO_NOT_LAUNCH_EXTRA)");
             return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
index 88ff379..f6aac38a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
@@ -17,9 +17,9 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
-import org.chromium.chrome.browser.notifications.NotificationConstants;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid;
 
@@ -164,11 +164,8 @@
     private void createNotification(int notificationId, int mediaType, String url) {
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                mContext.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                mContext.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setAutoCancel(false)
                         .setOngoing(true)
                         .setContentTitle(mContext.getString(R.string.app_name))
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
index 15dea8564..ca33b8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -35,7 +35,7 @@
             new CastMediaRouteProvider.Builder();
 
     // The pointer to the native object. Can be null only during tests.
-    private final long mNativeMediaRouterAndroid;
+    private final long mNativeMediaRouterAndroidBridge;
     private final List<MediaRouteProvider> mRouteProviders = new ArrayList<MediaRouteProvider>();
     private final Map<String, MediaRouteProvider> mRouteIdsToProviders =
             new HashMap<String, MediaRouteProvider>();
@@ -114,8 +114,9 @@
         for (List<MediaSink> s : sinksPerProvider.values()) allSinksPerSource.addAll(s);
 
         mSinksPerSource.put(sourceId, allSinksPerSource);
-        if (mNativeMediaRouterAndroid != 0) {
-            nativeOnSinksReceived(mNativeMediaRouterAndroid, sourceId, allSinksPerSource.size());
+        if (mNativeMediaRouterAndroidBridge != 0) {
+            nativeOnSinksReceived(
+                    mNativeMediaRouterAndroidBridge, sourceId, allSinksPerSource.size());
         }
     }
 
@@ -124,53 +125,53 @@
             String mediaRouteId, String mediaSinkId, int requestId, MediaRouteProvider provider,
             boolean wasLaunched) {
         mRouteIdsToProviders.put(mediaRouteId, provider);
-        if (mNativeMediaRouterAndroid != 0) {
-            nativeOnRouteCreated(mNativeMediaRouterAndroid, mediaRouteId, mediaSinkId, requestId,
-                    wasLaunched);
+        if (mNativeMediaRouterAndroidBridge != 0) {
+            nativeOnRouteCreated(mNativeMediaRouterAndroidBridge, mediaRouteId, mediaSinkId,
+                    requestId, wasLaunched);
         }
     }
 
     @Override
     public void onRouteRequestError(String errorText, int requestId) {
-        if (mNativeMediaRouterAndroid != 0) {
-            nativeOnRouteRequestError(mNativeMediaRouterAndroid, errorText, requestId);
+        if (mNativeMediaRouterAndroidBridge != 0) {
+            nativeOnRouteRequestError(mNativeMediaRouterAndroidBridge, errorText, requestId);
         }
     }
 
     @Override
     public void onRouteClosed(String mediaRouteId) {
-        if (mNativeMediaRouterAndroid != 0) {
-            nativeOnRouteClosed(mNativeMediaRouterAndroid, mediaRouteId);
+        if (mNativeMediaRouterAndroidBridge != 0) {
+            nativeOnRouteClosed(mNativeMediaRouterAndroidBridge, mediaRouteId);
         }
         mRouteIdsToProviders.remove(mediaRouteId);
     }
 
     @Override
     public void onRouteClosedWithError(String mediaRouteId, String message) {
-        if (mNativeMediaRouterAndroid != 0) {
-            nativeOnRouteClosedWithError(mNativeMediaRouterAndroid, mediaRouteId, message);
+        if (mNativeMediaRouterAndroidBridge != 0) {
+            nativeOnRouteClosedWithError(mNativeMediaRouterAndroidBridge, mediaRouteId, message);
         }
         mRouteIdsToProviders.remove(mediaRouteId);
     }
 
     @Override
     public void onMessageSentResult(boolean success, int callbackId) {
-        nativeOnMessageSentResult(mNativeMediaRouterAndroid, success, callbackId);
+        nativeOnMessageSentResult(mNativeMediaRouterAndroidBridge, success, callbackId);
     }
 
     @Override
     public void onMessage(String mediaRouteId, String message) {
-        nativeOnMessage(mNativeMediaRouterAndroid, mediaRouteId, message);
+        nativeOnMessage(mNativeMediaRouterAndroidBridge, mediaRouteId, message);
     }
 
     /**
      * Initializes the media router and its providers.
-     * @param nativeMediaRouterAndroid the handler for the native counterpart of this instance
+     * @param nativeMediaRouterAndroidBridge the handler for the native counterpart of this instance
      * @return an initialized {@link ChromeMediaRouter} instance
      */
     @CalledByNative
-    public static ChromeMediaRouter create(long nativeMediaRouterAndroid) {
-        ChromeMediaRouter router = new ChromeMediaRouter(nativeMediaRouterAndroid);
+    public static ChromeMediaRouter create(long nativeMediaRouterAndroidBridge) {
+        ChromeMediaRouter router = new ChromeMediaRouter(nativeMediaRouterAndroidBridge);
         MediaRouteProvider provider = sRouteProviderBuilder.create(router);
         if (provider != null) router.addMediaRouteProvider(provider);
 
@@ -326,7 +327,7 @@
     public void sendStringMessage(String routeId, String message, int callbackId) {
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
         if (provider == null) {
-            nativeOnMessageSentResult(mNativeMediaRouterAndroid, false, callbackId);
+            nativeOnMessageSentResult(mNativeMediaRouterAndroidBridge, false, callbackId);
             return;
         }
 
@@ -334,8 +335,8 @@
     }
 
     @VisibleForTesting
-    protected ChromeMediaRouter(long nativeMediaRouter) {
-        mNativeMediaRouterAndroid = nativeMediaRouter;
+    protected ChromeMediaRouter(long nativeMediaRouterAndroidBridge) {
+        mNativeMediaRouterAndroidBridge = nativeMediaRouterAndroidBridge;
     }
 
     @VisibleForTesting
@@ -356,19 +357,16 @@
     }
 
     native void nativeOnSinksReceived(
-            long nativeMediaRouterAndroid, String sourceUrn, int count);
-    native void nativeOnRouteCreated(
-            long nativeMediaRouterAndroid,
-            String mediaRouteId,
-            String mediaSinkId,
-            int createRouteRequestId,
-            boolean wasLaunched);
+            long nativeMediaRouterAndroidBridge, String sourceUrn, int count);
+    native void nativeOnRouteCreated(long nativeMediaRouterAndroidBridge, String mediaRouteId,
+            String mediaSinkId, int createRouteRequestId, boolean wasLaunched);
     native void nativeOnRouteRequestError(
-            long nativeMediaRouterAndroid, String errorText, int createRouteRequestId);
-    native void nativeOnRouteClosed(long nativeMediaRouterAndroid, String mediaRouteId);
+            long nativeMediaRouterAndroidBridge, String errorText, int createRouteRequestId);
+    native void nativeOnRouteClosed(long nativeMediaRouterAndroidBridge, String mediaRouteId);
     native void nativeOnRouteClosedWithError(
-            long nativeMediaRouterAndroid, String mediaRouteId, String message);
+            long nativeMediaRouterAndroidBridge, String mediaRouteId, String message);
     native void nativeOnMessageSentResult(
-            long nativeMediaRouterAndroid, boolean success, int callbackId);
-    native void nativeOnMessage(long nativeMediaRouterAndroid, String mediaRouteId, String message);
+            long nativeMediaRouterAndroidBridge, boolean success, int callbackId);
+    native void nativeOnMessage(
+            long nativeMediaRouterAndroidBridge, String mediaRouteId, String message);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
index fc92cfd..d387878a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
@@ -118,6 +118,7 @@
         if (mSession == null) return;
 
         if (mClientRecords.isEmpty()) {
+            for (String routeId : mRoutes.keySet()) mManager.onRouteClosed(routeId);
             mRoutes.clear();
         } else {
             mLastRemovedRouteRecord = mClientRecords.values().iterator().next();
@@ -321,11 +322,11 @@
     @Override
     public void closeRoute(String routeId) {
         MediaRoute route = mRoutes.get(routeId);
-
         if (route == null) return;
 
         if (mSession == null) {
             mRoutes.remove(routeId);
+            mManager.onRouteClosed(routeId);
             return;
         }
 
@@ -511,7 +512,8 @@
         return null;
     }
 
-    private void addRoute(MediaRoute route, String origin, int tabId) {
+    @VisibleForTesting
+    void addRoute(MediaRoute route, String origin, int tabId) {
         mRoutes.put(route.id, route);
 
         MediaSource source = MediaSource.from(route.sourceId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
index ed6bafe..060172aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
@@ -212,6 +212,7 @@
             Log.e(TAG, "Launch application failed with status: %s, %d, %s",
                     mSource.getApplicationId(), status.getStatusCode(), status.getStatusMessage());
             reportError();
+            return;
         }
 
         mState = STATE_LAUNCH_SUCCEEDED;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index adfebd9..659d14a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -38,6 +38,7 @@
 import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.AppHooks;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
@@ -770,11 +771,7 @@
 
     private void updateNotificationBuilder() {
         mNotificationBuilder = NotificationBuilderFactory.createChromeNotificationBuilder(
-                true /* preferCompat */, NotificationConstants.CHANNEL_ID_BROWSER,
-                getContext().getString(org.chromium.chrome.R.string.notification_category_browser),
-                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                getContext().getString(
-                        org.chromium.chrome.R.string.notification_category_group_general));
+                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER);
         setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder);
 
         mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.notificationSmallIcon);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
index b65b317..85c9587 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -204,6 +204,15 @@
     }
 
     /**
+     * Returns the content length saved for the number of days shown in the history summary.
+     * @return The content length saved.
+     */
+    public long getContentLengthSavedInHistorySummary() {
+        ContentLengths length = getContentLengths();
+        return Math.max(length.getOriginal() - length.getReceived(), 0);
+    }
+
+    /**
      * Returns the total HTTP content length saved.
      * @return The HTTP content length saved.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java
new file mode 100644
index 0000000..040d788
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java
@@ -0,0 +1,124 @@
+// 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.notifications;
+
+import android.annotation.TargetApi;
+import android.app.NotificationManager;
+import android.os.Build;
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Initializes our notification channels.
+ */
+public class ChannelsInitializer {
+    // To define a new channel, add the channel ID to this StringDef and add a new entry to
+    // PredefinedChannels.MAP below with the appropriate channel parameters.
+    @StringDef({CHANNEL_ID_BROWSER, CHANNEL_ID_SITES})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ChannelId {}
+    public static final String CHANNEL_ID_BROWSER = "browser";
+    public static final String CHANNEL_ID_SITES = "sites";
+
+    @StringDef({CHANNEL_GROUP_ID_GENERAL})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ChannelGroupId {}
+    static final String CHANNEL_GROUP_ID_GENERAL = "general";
+
+    // Map defined in static inner class so it's only initialized lazily.
+    @TargetApi(Build.VERSION_CODES.N) // for NotificationManager.IMPORTANCE_* constants
+    private static class PredefinedChannels {
+        private static final Map<String, Channel> MAP;
+        static {
+            Map<String, Channel> map = new HashMap<>();
+            map.put(CHANNEL_ID_BROWSER,
+                    new Channel(CHANNEL_ID_BROWSER,
+                            org.chromium.chrome.R.string.notification_category_browser,
+                            NotificationManager.IMPORTANCE_LOW, CHANNEL_GROUP_ID_GENERAL));
+            map.put(CHANNEL_ID_SITES,
+                    new Channel(CHANNEL_ID_SITES,
+                            org.chromium.chrome.R.string.notification_category_sites,
+                            NotificationManager.IMPORTANCE_DEFAULT, CHANNEL_GROUP_ID_GENERAL));
+            MAP = Collections.unmodifiableMap(map);
+        }
+    }
+
+    // Map defined in static inner class so it's only initialized lazily.
+    private static class PredefinedChannelGroups {
+        private static final Map<String, ChannelGroup> MAP;
+        static {
+            Map<String, ChannelGroup> map = new HashMap<>();
+            map.put(CHANNEL_GROUP_ID_GENERAL,
+                    new ChannelGroup(CHANNEL_GROUP_ID_GENERAL,
+                            org.chromium.chrome.R.string.notification_category_group_general));
+            MAP = Collections.unmodifiableMap(map);
+        }
+    }
+
+    private final NotificationManagerProxy mNotificationManager;
+
+    public ChannelsInitializer(NotificationManagerProxy notificationManagerProxy) {
+        mNotificationManager = notificationManagerProxy;
+    }
+
+    /**
+     * Ensures the given channel has been created on the notification manager so a notification
+     * can be safely posted to it. This should only be used for channels that are predefined in
+     * ChannelsInitializer.
+     *
+     * Calling this is a (potentially lengthy) no-op if the channel has already been created.
+     *
+     * @param channelId The ID of the channel to be initialized.
+     */
+    public void ensureInitialized(@ChannelId String channelId) {
+        if (!PredefinedChannels.MAP.containsKey(channelId)) {
+            throw new IllegalStateException("Could not initialize channel: " + channelId);
+        }
+        Channel channel = PredefinedChannels.MAP.get(channelId);
+        // Channel group must be created before the channel.
+        mNotificationManager.createNotificationChannelGroup(
+                PredefinedChannelGroups.MAP.get(channel.mGroupId));
+        mNotificationManager.createNotificationChannel(channel);
+    }
+
+    /**
+     * Helper class containing notification channel properties.
+     */
+    public static class Channel {
+        @ChannelId
+        final String mId;
+        final int mNameResId;
+        final int mImportance;
+        @ChannelGroupId
+        final String mGroupId;
+
+        Channel(@ChannelId String id, int nameResId, int importance,
+                @ChannelGroupId String groupId) {
+            this.mId = id;
+            this.mNameResId = nameResId;
+            this.mImportance = importance;
+            this.mGroupId = groupId;
+        }
+    }
+
+    /**
+     * Helper class containing notification channel group properties.
+     */
+    public static class ChannelGroup {
+        @ChannelGroupId
+        final String mId;
+        final int mNameResId;
+
+        ChannelGroup(@ChannelGroupId String id, int nameResId) {
+            this.mId = id;
+            this.mNameResId = nameResId;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
index cd8e7a14..b66d3dc39 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
@@ -141,13 +141,8 @@
         // API level of methods you call on the builder.
         // TODO(crbug.com/697104) We should probably use a Compat builder.
         ChromeNotificationBuilder builder =
-                NotificationBuilderFactory.createChromeNotificationBuilder(false /* preferCompat */,
-                        NotificationConstants.CHANNEL_ID_SITES,
-                        mContext.getString(
-                                org.chromium.chrome.R.string.notification_category_sites),
-                        NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                        mContext.getString(
-                                org.chromium.chrome.R.string.notification_category_group_general));
+                NotificationBuilderFactory.createChromeNotificationBuilder(
+                        false /* preferCompat */, ChannelsInitializer.CHANNEL_ID_SITES);
         builder.setTicker(mTickerText);
         builder.setContentIntent(mContentIntent);
         builder.setDeleteIntent(mDeleteIntent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
index 609be47..5b00acf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.notifications;
 
+import android.annotation.SuppressLint;
+import android.app.NotificationManager;
 import android.content.Context;
 
 import org.chromium.base.BuildInfo;
@@ -20,26 +22,29 @@
      *
      * TODO(crbug.com/704152) Remove this once we've updated to revision 26 of the support library.
      * Then we can use NotificationCompat.Builder and set the channel directly everywhere.
+     * Although we will still need to ensure the channel is always initialized first.
      *
      * @param preferCompat true if a NotificationCompat.Builder is preferred.
      *                     A Notification.Builder will be used regardless on Android O.
-     * @param channelId The ID of the channel the notification should be posted to.
-     * @param channelName The name of the channel the notification should be posted to.
-     *                    Used to create the channel if a channel with channelId does not exist.
-     * @param channelGroupId The ID of the channel group the channel belongs to. Used when creating
-     *                       the channel if a channel with channelId does not exist.
-     * @param channelGroupName The name of the channel group the channel belongs to.
-     *                         Used to create the channel group if a channel group with
-     *                         channelGroupId does not already exist.
+     * @param channelId The ID of the channel the notification should be posted to. This channel
+     *                  will be created if it did not already exist. Must be a known channel within
+     *                  {@link ChannelsInitializer#ensureInitialized(String)}.
      */
-    public static ChromeNotificationBuilder createChromeNotificationBuilder(boolean preferCompat,
-            String channelId, String channelName, String channelGroupId, String channelGroupName) {
+    public static ChromeNotificationBuilder createChromeNotificationBuilder(
+            boolean preferCompat, @ChannelsInitializer.ChannelId String channelId) {
         Context context = ContextUtils.getApplicationContext();
         if (BuildInfo.isAtLeastO()) {
-            return new NotificationBuilderForO(ContextUtils.getApplicationContext(), channelId,
-                    channelName, channelGroupId, channelGroupName);
+            return createNotificationBuilderForO(channelId, context);
         }
         return preferCompat ? new NotificationCompatBuilder(context)
                             : new NotificationBuilder(context);
     }
+
+    @SuppressLint("NewApi") // for Context.getSystemService(Class)
+    private static ChromeNotificationBuilder createNotificationBuilderForO(
+            @ChannelsInitializer.ChannelId String channelId, Context context) {
+        return new NotificationBuilderForO(context, channelId,
+                new ChannelsInitializer(new NotificationManagerProxyImpl(
+                        context.getSystemService(NotificationManager.class))));
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderForO.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderForO.java
index 2daa053..b207b3a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderForO.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderForO.java
@@ -6,13 +6,11 @@
 
 import android.annotation.TargetApi;
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.content.Context;
 
 import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
@@ -23,77 +21,17 @@
 public class NotificationBuilderForO extends NotificationBuilder {
     private static final String TAG = "NotifBuilderForO";
 
-    public NotificationBuilderForO(Context context, String channelId, String channelName,
-            String channelGroupId, String channelGroupName) {
+    public NotificationBuilderForO(Context context, @ChannelsInitializer.ChannelId String channelId,
+            ChannelsInitializer channelsInitializer) {
         super(context);
         assert BuildInfo.isAtLeastO();
-        NotificationManager notificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        /*
-        The code in the try-block uses reflection in order to compile as it calls APIs newer than
-        our target version of Android. The equivalent code without reflection is as follows:
-
-            notificationManager.createNotificationChannelGroup(
-                    new NotificationChannelGroup(channelGroupId, channelGroupName));
-            NotificationChannel channel = new NotificationChannel(
-                    channelId, channelName, importance);
-            channel.setGroup(channelGroupId);
-            channel.setShowBadge(false);
-            notificationManager.createNotificationChannel(channel);
-            notificationBuilder.setChannel(channelId);
-
-        Note this whole class can be removed once we target O, and channels may be set
-        inline where notifications are created, using the pattern above.
-
-        In the longer term we may wish to initialize our channels in response to BOOT_COMPLETED or
-        ACTION_PACKAGE_REPLACED, as per Android guidelines.
-         */
+        channelsInitializer.ensureInitialized(channelId);
+        // TODO(crbug.com/707804) Stop using reflection once compileSdkVersion is high enough.
         try {
-            // Create channel group
-            Class<?> channelGroupClass = Class.forName("android.app.NotificationChannelGroup");
-            Constructor<?> channelGroupConstructor =
-                    channelGroupClass.getDeclaredConstructor(String.class, CharSequence.class);
-            Object channelGroup =
-                    channelGroupConstructor.newInstance(channelGroupId, channelGroupName);
-
-            // Register channel group
-            Method createNotificationChannelGroupMethod = notificationManager.getClass().getMethod(
-                    "createNotificationChannelGroup", channelGroupClass);
-            createNotificationChannelGroupMethod.invoke(notificationManager, channelGroup);
-
-            // Create channel
-            Class<?> channelClass = Class.forName("android.app.NotificationChannel");
-            Constructor<?> channelConstructor = channelClass.getDeclaredConstructor(
-                    String.class, CharSequence.class, int.class);
-            Object channel = channelConstructor.newInstance(
-                    channelId, channelName, getChannelImportance(channelId));
-
-            // Set group on channel
-            Method setGroupMethod = channelClass.getMethod("setGroup", String.class);
-            setGroupMethod.invoke(channel, channelGroupId);
-
-            // Set channel to not badge on app icon
-            Method setShowBadgeMethod = channelClass.getMethod("setShowBadge", boolean.class);
-            setShowBadgeMethod.invoke(channel, false);
-
-            // Register channel
-            Method createNotificationChannelMethod = notificationManager.getClass().getMethod(
-                    "createNotificationChannel", channelClass);
-            createNotificationChannelMethod.invoke(notificationManager, channel);
-
-            // Set channel on builder
-            Method setChannelMethod =
-                    Notification.Builder.class.getMethod("setChannel", String.class);
-            setChannelMethod.invoke(mBuilder, channelId);
-        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
-                | InstantiationException | InvocationTargetException e) {
-            Log.e(TAG, "Error initializing notification builder:", e);
+            Method setChannel = Notification.Builder.class.getMethod("setChannel", String.class);
+            setChannel.invoke(mBuilder, channelId);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Log.e(TAG, "Error setting channel on notification builder:", e);
         }
     }
-
-    private static int getChannelImportance(String channelId) {
-        return NotificationConstants.CHANNEL_ID_SITES.equals(channelId)
-                ? NotificationManager.IMPORTANCE_DEFAULT
-                : NotificationManager.IMPORTANCE_LOW;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
index eca0b774..cb04284 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -86,8 +86,4 @@
     // For example, 'Web:chromium.org' for a notification from chromium.org.
     static final String GROUP_WEB_PREFIX = "Web:";
 
-    // Notification channel ids.
-    public static final String CHANNEL_ID_BROWSER = "browser";
-    public static final String CHANNEL_ID_SITES = "sites";
-    public static final String CHANNEL_GROUP_ID_GENERAL = "general";
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
index fd9ae67..7f41308 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
@@ -16,6 +16,9 @@
     void cancel(int id);
     void cancel(String tag, int id);
     void cancelAll();
+    void createNotificationChannel(ChannelsInitializer.Channel channel);
+    void createNotificationChannelGroup(ChannelsInitializer.ChannelGroup channelGroup);
+
     void notify(int id, Notification notification);
     void notify(String tag, int id, Notification notification);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
index b05fd0047..46d2a10 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
@@ -7,11 +7,20 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 
+import org.chromium.base.BuildInfo;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 /**
  * Default implementation of the NotificationManagerProxy, which passes through all calls to the
  * normal Android Notification Manager.
  */
 public class NotificationManagerProxyImpl implements NotificationManagerProxy {
+    private static final String TAG = "NotifManagerProxy";
     private final NotificationManager mNotificationManager;
 
     public NotificationManagerProxyImpl(NotificationManager notificationManager) {
@@ -34,6 +43,75 @@
     }
 
     @Override
+    public void createNotificationChannel(ChannelsInitializer.Channel channel) {
+        assert BuildInfo.isAtLeastO();
+        /*
+        The code in the try-block uses reflection in order to compile as it calls APIs newer than
+        our compileSdkVersion of Android. The equivalent code without reflection looks like this:
+
+            channel.setGroup(channelGroupId);
+            channel.setShowBadge(false);
+            mNotificationManager.createNotificationChannel(channel);
+         */
+        // TODO(crbug.com/707804) Stop using reflection once compileSdkVersion is high enough.
+        try {
+            // Create channel
+            Class<?> channelClass = Class.forName("android.app.NotificationChannel");
+            Constructor<?> channelConstructor = channelClass.getDeclaredConstructor(
+                    String.class, CharSequence.class, int.class);
+            Object channelObject = channelConstructor.newInstance(channel.mId,
+                    ContextUtils.getApplicationContext().getString(channel.mNameResId),
+                    channel.mImportance);
+
+            // Set group on channel
+            Method setGroupMethod = channelClass.getMethod("setGroup", String.class);
+            setGroupMethod.invoke(channelObject, channel.mGroupId);
+
+            // Set channel to not badge on app icon
+            Method setShowBadgeMethod = channelClass.getMethod("setShowBadge", boolean.class);
+            setShowBadgeMethod.invoke(channelObject, false);
+
+            // Register channel
+            Method createNotificationChannelMethod = mNotificationManager.getClass().getMethod(
+                    "createNotificationChannel", channelClass);
+            createNotificationChannelMethod.invoke(mNotificationManager, channelObject);
+
+        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+                | InstantiationException | InvocationTargetException e) {
+            Log.e(TAG, "Error initializing notification channel:", e);
+        }
+    }
+
+    @Override
+    public void createNotificationChannelGroup(ChannelsInitializer.ChannelGroup channelGroup) {
+        assert BuildInfo.isAtLeastO();
+        /*
+        The code in the try-block uses reflection in order to compile as it calls APIs newer than
+        our compileSdkVersion of Android. The equivalent code without reflection looks like this:
+
+            mNotificationManager.createNotificationChannelGroup(channelGroup);
+         */
+        // TODO(crbug.com/707804) Stop using reflection once compileSdkVersion is high enough.
+        try {
+            // Create channel group
+            Class<?> channelGroupClass = Class.forName("android.app.NotificationChannelGroup");
+            Constructor<?> channelGroupConstructor =
+                    channelGroupClass.getDeclaredConstructor(String.class, CharSequence.class);
+            Object channelGroupObject = channelGroupConstructor.newInstance(channelGroup.mId,
+                    ContextUtils.getApplicationContext().getString(channelGroup.mNameResId));
+
+            // Register channel group
+            Method createNotificationChannelGroupMethod = mNotificationManager.getClass().getMethod(
+                    "createNotificationChannelGroup", channelGroupClass);
+            createNotificationChannelGroupMethod.invoke(mNotificationManager, channelGroupObject);
+
+        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+                | InstantiationException | InvocationTargetException e) {
+            Log.e(TAG, "Error initializing notification channel group:", e);
+        }
+    }
+
+    @Override
     public void notify(int id, Notification notification) {
         mNotificationManager.notify(id, notification);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
index dbda582..9cb36919 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
@@ -25,13 +25,8 @@
         // API level of methods you call on the builder.
         // TODO(crbug.com/697104) We should probably use a Compat builder.
         ChromeNotificationBuilder builder =
-                NotificationBuilderFactory.createChromeNotificationBuilder(false /* preferCompat */,
-                        NotificationConstants.CHANNEL_ID_SITES,
-                        mContext.getString(
-                                org.chromium.chrome.R.string.notification_category_sites),
-                        NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                        mContext.getString(
-                                org.chromium.chrome.R.string.notification_category_group_general));
+                NotificationBuilderFactory.createChromeNotificationBuilder(
+                        false /* preferCompat */, ChannelsInitializer.CHANNEL_ID_SITES);
 
         builder.setContentTitle(mTitle);
         builder.setContentText(mBody);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
index f5847694..1bc98b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
@@ -25,9 +25,9 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
-import org.chromium.chrome.browser.notifications.NotificationConstants;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsNotificationAction;
 import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsNotificationOptOut;
@@ -172,11 +172,8 @@
                 0);
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                context.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                context.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setAutoCancel(true)
                         .setContentIntent(contentIntent)
                         .setDeleteIntent(deleteIntent)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
index 28a5559..3d5f87c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
@@ -212,6 +212,7 @@
 
         mPendingWebAppManifestsCount = webAppManifestUris.length;
         for (int i = 0; i < webAppManifestUris.length; i++) {
+            assert webAppManifestUris[i] != null;
             mDownloader.downloadWebAppManifest(webAppManifestUris[i], this);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
index d0c4169c..6070779 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
@@ -13,6 +13,9 @@
 import android.support.graphics.drawable.VectorDrawableCompat;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.ScaleAnimation;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -27,6 +30,12 @@
  * A container class for a view showing a photo in the Photo Picker.
  */
 public class PickerBitmapView extends SelectableItemView<PickerBitmap> {
+    // The length of the image selection animation (in ms).
+    private static final int ANIMATION_DURATION = 100;
+
+    // The length of the fade in animation (in ms).
+    private static final int IMAGE_FADE_IN_DURATION = 200;
+
     // Our context.
     private Context mContext;
 
@@ -58,6 +67,9 @@
     // Whether the image has been loaded already.
     private boolean mImageLoaded;
 
+    // The amount to use for the border.
+    private int mBorder;
+
     /**
      * Constructor for inflating from XML.
      */
@@ -121,6 +133,34 @@
     @Override
     public void onSelectionStateChange(List<PickerBitmap> selectedItems) {
         updateSelectionState();
+
+        if (!isPictureTile()) return;
+
+        boolean selected = selectedItems.contains(mBitmapDetails);
+        boolean checked = super.isChecked();
+        boolean needsResize = selected != checked;
+        int size = selected && !checked ? mCategoryView.getImageSize() - 2 * mBorder
+                                        : mCategoryView.getImageSize();
+        if (needsResize) {
+            float start;
+            float end;
+            if (size != mCategoryView.getImageSize()) {
+                start = 1f;
+                end = 0.8f;
+            } else {
+                start = 0.8f;
+                end = 1f;
+            }
+
+            Animation animation = new ScaleAnimation(
+                    start, end, // Values for x axis.
+                    start, end, // Values for y axis.
+                    Animation.RELATIVE_TO_SELF, 0.5f, // Pivot X-axis type and value.
+                    Animation.RELATIVE_TO_SELF, 0.5f); // Pivot Y-axis type and value.
+            animation.setDuration(ANIMATION_DURATION);
+            animation.setFillAfter(true); // Keep the results of the animation.
+            mIconView.startAnimation(animation);
+        }
     }
 
     /**
@@ -132,6 +172,8 @@
         mCategoryView = categoryView;
         mSelectionDelegate = mCategoryView.getSelectionDelegate();
         setSelectionDelegate(mSelectionDelegate);
+
+        mBorder = (int) getResources().getDimension(R.dimen.photo_picker_selected_padding);
     }
 
     /**
@@ -182,13 +224,22 @@
     }
 
     /**
-     * Sets a thumbnail bitmap for the current view.
+     * Sets a thumbnail bitmap for the current view and ensures the selection border is showing, if
+     * the image has already been selected.
      * @param thumbnail The Bitmap to use for the icon ImageView.
      * @return True if no image was loaded before (e.g. not even a low-res image).
      */
     public boolean setThumbnailBitmap(Bitmap thumbnail) {
         mIconView.setImageBitmap(thumbnail);
 
+        // If the tile has been selected before the bitmap has loaded, make sure it shows up with
+        // a selection border on load.
+        if (super.isChecked()) {
+            mIconView.getLayoutParams().height = imageSizeWithBorders();
+            mIconView.getLayoutParams().width = imageSizeWithBorders();
+            addPaddingToParent(mIconView, mBorder);
+        }
+
         boolean noImageWasLoaded = !mImageLoaded;
         mImageLoaded = true;
         updateSelectionState();
@@ -196,6 +247,20 @@
         return noImageWasLoaded;
     }
 
+    /** Returns the size of the image plus the pre-determined border on each side. */
+    private int imageSizeWithBorders() {
+        return mCategoryView.getImageSize() - 2 * mBorder;
+    }
+
+    /**
+     * Initiates fading in of the thumbnail. Note, this should not be called if a grainy version of
+     * the thumbnail was loaded from cache. Otherwise a flash will appear.
+     */
+    public void fadeInThumbnail() {
+        mIconView.setAlpha(0.0f);
+        mIconView.animate().alpha(1.0f).setDuration(IMAGE_FADE_IN_DURATION).start();
+    }
+
     /**
      * Resets the view to its starting state, which is necessary when the view is about to be
      * re-used.
@@ -208,6 +273,17 @@
     }
 
     /**
+     * Adds padding to the parent of the |view|.
+     * @param view The child view of the view to receive the padding.
+     * @param padding The amount of padding to use (in pixels).
+     */
+    private static void addPaddingToParent(View view, int padding) {
+        ViewGroup layout = (ViewGroup) view.getParent();
+        layout.setPadding(padding, padding, padding, padding);
+        layout.requestLayout();
+    }
+
+    /**
      * Updates the selection controls for this view.
      */
     private void updateSelectionState() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
index b4151f27..17dde3f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -112,7 +112,8 @@
         RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(mContext, mColumns);
         mRecyclerView.setHasFixedSize(true);
         mRecyclerView.setLayoutManager(mLayoutManager);
-        // TODO(finnur): Implement grid spacing.
+        mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(mColumns, mPadding));
+
         // TODO(finnur): Implement caching.
         // TODO(finnur): Remove this once the decoder service is in place.
         prepareBitmaps();
@@ -226,4 +227,41 @@
 
         mListener.onPickerUserAction(PhotoPickerListener.Action.PHOTOS_SELECTED, photos);
     }
+
+    /**
+     * A class for implementing grid spacing between items.
+     */
+    private class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
+        // The number of spans to account for.
+        private int mSpanCount;
+
+        // The amount of spacing to use.
+        private int mSpacing;
+
+        public GridSpacingItemDecoration(int spanCount, int spacing) {
+            mSpanCount = spanCount;
+            mSpacing = spacing;
+        }
+
+        @Override
+        public void getItemOffsets(
+                Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+            int left = 0, right = 0, top = 0, bottom = 0;
+            int position = parent.getChildAdapterPosition(view);
+
+            if (position >= 0) {
+                int column = position % mSpanCount;
+
+                left = mSpacing - ((column * mSpacing) / mSpanCount);
+                right = (column + 1) * mSpacing / mSpanCount;
+
+                if (position < mSpanCount) {
+                    top = mSpacing;
+                }
+                bottom = mSpacing;
+            }
+
+            outRect.set(left, top, right, bottom);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBroadcastService.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBroadcastService.java
index 5f6588f..caf2d00d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBroadcastService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBroadcastService.java
@@ -20,6 +20,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
@@ -150,11 +151,8 @@
                 (NotificationManager) getSystemService(NOTIFICATION_SERVICE));
         ChromeNotificationBuilder notificationBuilder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                context.getString(R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                context.getString(R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setSmallIcon(R.drawable.ic_image_white_24dp)
                         .setContentTitle(getString(R.string.physical_web_broadcast_notification))
                         .setContentText(displayUrl)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java
index cac6293..93f08a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java
@@ -25,14 +25,14 @@
      */
     public ButtonPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
-        setLayoutResource(R.layout.preference_button);
+        setLayoutResource(R.layout.button_preference_layout);
+        setWidgetLayoutResource(R.layout.button_preference_button);
         setSelectable(false);
     }
 
     @Override
     protected void onBindView(View view) {
         super.onBindView(view);
-
         Button button = (Button) view.findViewById(R.id.button_preference);
         button.setText(this.getTitle());
         button.setOnClickListener(new View.OnClickListener() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
index 3c8ddc5..c3afb169 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SignInPreference.java
@@ -38,6 +38,7 @@
         implements SignInAllowedObserver, ProfileDownloader.Observer,
                    AndroidSyncSettings.AndroidSyncSettingsObserver, SyncStateChangedListener {
     private boolean mViewEnabled;
+    private boolean mShowingPromo;
 
     /**
      * Constructor for inflating from XML.
@@ -84,10 +85,18 @@
         String accountName = ChromeSigninController.get().getSignedInAccountName();
         if (SigninManager.get(getContext()).isSigninDisabledByPolicy()) {
             setupSigninDisabled();
+            mShowingPromo = false;
         } else if (accountName == null) {
             setupNotSignedIn();
+
+            if (!mShowingPromo) {
+                // This user action should be recorded when message with sign-in prompt is shown
+                RecordUserAction.record("Signin_Impression_FromSettings");
+            }
+            mShowingPromo = true;
         } else {
             setupSignedIn(accountName);
+            mShowingPromo = false;
         }
 
         setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -97,10 +106,6 @@
                         getContext(), SigninAccessPoint.SETTINGS);
             }
         });
-
-        if (accountName == null && SigninManager.get(getContext()).isSignInAllowed()) {
-            RecordUserAction.record("Signin_Impression_FromSettings");
-        }
     }
 
     private void setupSigninDisabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
index 430ba0a..62e8031 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
@@ -47,7 +47,8 @@
 
         if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
             String dataSaved = Formatter.formatShortFileSize(getContext(),
-                    DataReductionProxySettings.getInstance().getTotalHttpContentLengthSaved());
+                    DataReductionProxySettings.getInstance()
+                            .getContentLengthSavedInHistorySummary());
 
             long millisSinceEpoch =
                     DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index eeb7098..e6848cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -5,8 +5,6 @@
 package org.chromium.chrome.browser.searchwidget;
 
 import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
 import android.support.customtabs.CustomTabsIntent;
@@ -16,7 +14,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.IntentHandler;
@@ -27,8 +24,6 @@
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.omnibox.AutocompleteController;
-import org.chromium.chrome.browser.omnibox.UrlBar;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
 import org.chromium.chrome.browser.tab.Tab;
@@ -38,23 +33,12 @@
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.ActivityWindowAndroid;
 
 /** Prototype that queries the user's default search engine and shows autocomplete suggestions. */
 public class SearchActivity extends AsyncInitializationActivity
         implements SnackbarManageable, SearchActivityLocationBarLayout.Delegate,
                    View.OnLayoutChangeListener {
-    private static final String TAG = "searchwidget";
-
-    /** Padding gleaned from the background Drawable of the search box. */
-    private final Rect mSearchBoxPadding = new Rect();
-
-    /** Medium margin/padding used for the search box. */
-    private int mSpacingMedium;
-
-    /** Large margin/padding used for the search box. */
-    private int mSpacingLarge;
 
     /** Main content view. */
     private ViewGroup mContentView;
@@ -68,29 +52,14 @@
     /** The View that represents the search box. */
     private SearchActivityLocationBarLayout mSearchBox;
 
-    private UrlBar mUrlBar;
-
-    private SearchBoxDataProvider mSearchBoxDataProvider;
-
-    private SnackbarManager mSnackbarManager;
     private ActivityWindowAndroid mWindowAndroid;
+    private SnackbarManager mSnackbarManager;
+    private SearchBoxDataProvider mSearchBoxDataProvider;
     private Tab mTab;
 
     @Override
     public void backKeyPressed() {
-        finish();
-    }
-
-    @Override
-    public void onStop() {
-        finish();
-        super.onStop();
-    }
-
-    @Override
-    public void finish() {
-        super.finish();
-        overridePendingTransition(0, android.R.anim.fade_out);
+        cancelSearch();
     }
 
     @Override
@@ -101,43 +70,39 @@
     @Override
     public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent intent) {
         if (super.onActivityResultWithNative(requestCode, resultCode, intent)) return true;
-        boolean result = mWindowAndroid.onActivityResult(requestCode, resultCode, intent);
+        return mWindowAndroid.onActivityResult(requestCode, resultCode, intent);
+    }
 
-        // The voice query should have completed.  If the voice recognition isn't confident about
-        // what it heard, it puts the query into the omnibox.  We need to focus it at that point.
-        if (mUrlBar != null) focusTextBox(false);
-
-        return result;
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, String[] permissions, int[] grantResults) {
+        // TODO(dfalcantara): This is from ChromeWindow.  It should be moved into the
+        //                    AsyncInitializationActivity, but that has a lot of subclasses that
+        //                    don't need a WindowAndroid.
+        if (mWindowAndroid != null) {
+            if (mWindowAndroid.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
+                return;
+            }
+        }
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
     }
 
     @Override
     protected void setContentView() {
-        initializeDimensions();
-
         mWindowAndroid = new ActivityWindowAndroid(this);
         mSnackbarManager = new SnackbarManager(this, null);
         mSearchBoxDataProvider = new SearchBoxDataProvider();
 
-        mContentView = createContentView(mSearchBox);
-        mContentView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Finish the Activity if the user clicks on the scrim.
-                finish();
-            }
-        });
+        mContentView = createContentView();
 
         // Build the search box.
         mSearchBox = (SearchActivityLocationBarLayout) mContentView.findViewById(
                 R.id.search_location_bar);
         mSearchBox.setDelegate(this);
-        mSearchBox.setPadding(mSpacingLarge, mSpacingMedium, mSpacingLarge, mSpacingMedium);
-        mSearchBox.initializeControls(new WindowDelegate(getWindow()), mWindowAndroid);
-        mSearchBox.setUrlBarFocusable(true);
         mSearchBox.setToolbarDataProvider(mSearchBoxDataProvider);
+        mSearchBox.initializeControls(new WindowDelegate(getWindow()), mWindowAndroid);
 
         setContentView(mContentView);
-        mUrlBar = (UrlBar) mSearchBox.findViewById(R.id.url_bar);
     }
 
     @Override
@@ -151,10 +116,9 @@
         mTab.initialize(WebContentsFactory.createWebContents(false, false), null,
                 new TabDelegateFactory(), false, false);
         mTab.loadUrl(new LoadUrlParams("about:blank"));
+
         mSearchBoxDataProvider.onNativeLibraryReady(mTab);
         mSearchBox.onNativeLibraryReady();
-        mSearchBox.setAutocompleteProfile(Profile.getLastUsedProfile().getOriginalProfile());
-        mSearchBox.setShowCachedZeroSuggestResults(true);
 
         if (mQueuedUrl != null) loadUrl(mQueuedUrl);
 
@@ -172,8 +136,7 @@
 
         AutocompleteController.nativePrefetchZeroSuggestResults();
         CustomTabsConnection.getInstance(getApplication()).warmup(0);
-
-        if (!isVoiceSearchIntent() && mUrlBar.isFocused()) mSearchBox.onUrlFocusChange(true);
+        mSearchBox.onDeferredStartup(isVoiceSearchIntent());
     }
 
     @Override
@@ -199,11 +162,7 @@
     }
 
     private void beginQuery() {
-        if (isVoiceSearchIntent()) {
-            mSearchBox.startVoiceRecognition();
-        } else {
-            focusTextBox(true);
-        }
+        mSearchBox.beginQuery(isVoiceSearchIntent());
     }
 
     @Override
@@ -217,24 +176,6 @@
         return true;
     }
 
-    private void focusTextBox(boolean clearQuery) {
-        if (mIsNativeReady) mSearchBox.onUrlFocusChange(true);
-
-        if (clearQuery) {
-            mUrlBar.setIgnoreTextChangesForAutocomplete(true);
-            mUrlBar.setUrl("", null);
-            mUrlBar.setIgnoreTextChangesForAutocomplete(false);
-        }
-        mUrlBar.setCursorVisible(true);
-        mUrlBar.setSelection(0, mUrlBar.getText().length());
-        new Handler().post(new Runnable() {
-            @Override
-            public void run() {
-                UiUtils.showKeyboard(mUrlBar);
-            }
-        });
-    }
-
     @Override
     public void loadUrl(String url) {
         // Wait until native has loaded.
@@ -263,20 +204,25 @@
             IntentHandler.addTrustedIntentExtras(intent);
         }
 
-        // TODO(dfalcantara): Make IntentUtils take in an ActivityOptions bundle once public.
-        startActivity(intent,
+        IntentUtils.safeStartActivity(this, intent,
                 ActivityOptionsCompat
                         .makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out)
                         .toBundle());
         finish();
     }
 
-    private ViewGroup createContentView(final View searchBox) {
+    private ViewGroup createContentView() {
         assert mContentView == null;
 
         ViewGroup contentView = (ViewGroup) LayoutInflater.from(this).inflate(
                 R.layout.search_activity, null, false);
         contentView.addOnLayoutChangeListener(this);
+        contentView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                cancelSearch();
+            }
+        });
         return contentView;
     }
 
@@ -287,19 +233,6 @@
         beginLoadingLibrary();
     }
 
-    private void initializeDimensions() {
-        // Cache the padding of the Drawable that is used as the background for the search box.
-        Drawable searchBackground =
-                ApiCompatibilityUtils.getDrawable(getResources(), R.drawable.card_single);
-        searchBackground.getPadding(mSearchBoxPadding);
-
-        // TODO(dfalcantara): Add values to the XML files instead of reusing random ones.
-        mSpacingMedium =
-                getResources().getDimensionPixelSize(R.dimen.location_bar_incognito_badge_padding);
-        mSpacingLarge =
-                getResources().getDimensionPixelSize(R.dimen.contextual_search_peek_promo_padding);
-    }
-
     private void beginLoadingLibrary() {
         beginQuery();
         mHandler.post(new Runnable() {
@@ -311,4 +244,9 @@
         ChromeBrowserInitializer.getInstance(getApplicationContext())
                 .handlePreNativeStartup(SearchActivity.this);
     }
+
+    private void cancelSearch() {
+        finish();
+        overridePendingTransition(0, R.anim.activity_close_exit);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 6d6ab8d..bd189f4b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -5,11 +5,14 @@
 package org.chromium.chrome.browser.searchwidget;
 
 import android.content.Context;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.view.View;
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.ui.UiUtils;
 
 /** Implementation of the {@link LocationBarLayout} that is displayed for widget searches. */
 public class SearchActivityLocationBarLayout extends LocationBarLayout {
@@ -26,8 +29,16 @@
 
     public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setUrlBarFocusable(true);
+
+        // TODO(dfalcantara): Get rid of any possibility of inflating the G in a layout.
         View gContainer = findViewById(R.id.google_g_container);
         if (gContainer != null) gContainer.setVisibility(View.GONE);
+
+        // TODO(dfalcantara): Find the correct way to do this.
+        int spacingLarge =
+                getResources().getDimensionPixelSize(R.dimen.contextual_search_peek_promo_padding);
+        setPadding(spacingLarge, 0, spacingLarge, 0);
     }
 
     /** Set the {@link Delegate}. */
@@ -54,4 +65,42 @@
     public void setUrlToPageUrl() {
         // Explicitly do nothing.  The tab is invisible, so showing its URL would be confusing.
     }
+
+    @Override
+    public void onNativeLibraryReady() {
+        super.onNativeLibraryReady();
+        setAutocompleteProfile(Profile.getLastUsedProfile().getOriginalProfile());
+        setShowCachedZeroSuggestResults(true);
+    }
+
+    /** Called when the SearchActivity has finished initialization. */
+    void onDeferredStartup(boolean isVoiceSearchIntent) {
+        if (isVoiceSearchIntent && mUrlBar.isFocused()) onUrlFocusChange(true);
+    }
+
+    /** Begins a new query. */
+    void beginQuery(boolean isVoiceSearchIntent) {
+        if (isVoiceSearchIntent) {
+            startVoiceRecognition();
+        } else {
+            focusTextBox();
+        }
+    }
+
+    private void focusTextBox() {
+        if (mNativeInitialized) onUrlFocusChange(true);
+
+        mUrlBar.setIgnoreTextChangesForAutocomplete(true);
+        mUrlBar.setUrl("", null);
+        mUrlBar.setIgnoreTextChangesForAutocomplete(false);
+
+        mUrlBar.setCursorVisible(true);
+        mUrlBar.setSelection(0, mUrlBar.getText().length());
+        new Handler().post(new Runnable() {
+            @Override
+            public void run() {
+                UiUtils.showKeyboard(mUrlBar);
+            }
+        });
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
index 0d3f4e7..a31bca8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
@@ -103,13 +104,8 @@
         // notification id ensures there's only one sync notification at a time.
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
-                        .createChromeNotificationBuilder(true /* preferCompat */,
-                                NotificationConstants.CHANNEL_ID_BROWSER,
-                                mApplicationContext.getString(
-                                        R.string.notification_category_browser),
-                                NotificationConstants.CHANNEL_GROUP_ID_GENERAL,
-                                mApplicationContext.getString(
-                                        R.string.notification_category_group_general))
+                        .createChromeNotificationBuilder(
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
                         .setAutoCancel(true)
                         .setContentIntent(contentIntent)
                         .setContentTitle(title)
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 2e29837..b98c6c7 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
@@ -5,11 +5,9 @@
 package org.chromium.chrome.browser.toolbar;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Rect;
-import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.v7.widget.Toolbar;
 import android.util.AttributeSet;
@@ -81,10 +79,10 @@
     private static final float TAB_SWITCHER_TOOLBAR_ALPHA = 0.7f;
 
     /** The white version of the toolbar handle; used for dark themes and incognito. */
-    private final Bitmap mHandleLight;
+    private final Drawable mHandleLight;
 
     /** The dark version of the toolbar handle; this is the default handle to use. */
-    private final Bitmap mHandleDark;
+    private final Drawable mHandleDark;
 
     /** A handle to the bottom sheet. */
     private BottomSheet mBottomSheet;
@@ -114,13 +112,10 @@
     public BottomToolbarPhone(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        int defaultHandleColor =
-                ApiCompatibilityUtils.getColor(getResources(), R.color.black_alpha_40);
-        mHandleDark = generateHandleBitmap(defaultHandleColor);
-
-        int lightHandleColor =
-                ApiCompatibilityUtils.getColor(getResources(), R.color.white_alpha_50);
-        mHandleLight = generateHandleBitmap(lightHandleColor);
+        mHandleDark = ApiCompatibilityUtils.getDrawable(
+                context.getResources(), R.drawable.toolbar_handle_dark);
+        mHandleLight = ApiCompatibilityUtils.getDrawable(
+                context.getResources(), R.drawable.toolbar_handle_light);
     }
 
     @Override
@@ -225,7 +220,7 @@
         // The toolbar handle is part of the control container so it can draw on top of the other
         // toolbar views. Get the root view and search for the handle.
         mToolbarHandleView = (ImageView) getRootView().findViewById(R.id.toolbar_handle);
-        mToolbarHandleView.setImageBitmap(mHandleDark);
+        mToolbarHandleView.setImageDrawable(mHandleDark);
     }
 
     @Override
@@ -240,7 +235,7 @@
         // switch color based on the static tab's theme color. This is done so fade in/out looks
         // correct.
         boolean isLight = ColorUtils.shouldUseLightForegroundOnBackground(getTabThemeColor());
-        mToolbarHandleView.setImageBitmap(isLight ? mHandleLight : mHandleDark);
+        mToolbarHandleView.setImageDrawable(isLight ? mHandleLight : mHandleDark);
     }
 
     @Override
@@ -251,33 +246,6 @@
         out.top -= getExtraTopMargin() * mUrlExpansionPercent;
     }
 
-    /**
-     * Generate the bitmap used as the handle on the toolbar. This indicates that the toolbar can
-     * be pulled up.
-     * @return The handle as a bitmap.
-     */
-    private Bitmap generateHandleBitmap(int handleColor) {
-        int handleWidth = getResources().getDimensionPixelSize(R.dimen.toolbar_handle_width);
-        int handleHeight = getResources().getDimensionPixelSize(R.dimen.toolbar_handle_height);
-
-        Bitmap handle = Bitmap.createBitmap(handleWidth, handleHeight, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(handle);
-
-        // Clear the canvas to be completely transparent.
-        canvas.drawARGB(0, 0, 0, 0);
-
-        Paint paint = new Paint();
-        paint.setColor(handleColor);
-        paint.setAntiAlias(true);
-
-        RectF rect = new RectF(0, 0, handleWidth, handleHeight);
-
-        // Use height / 2 for the corner radius so the handle always takes the shape of a pill.
-        canvas.drawRoundRect(rect, handleHeight / 2f, handleHeight / 2f, paint);
-
-        return handle;
-    }
-
     @Override
     protected boolean shouldDrawLocationBar() {
         return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/IntentUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/util/IntentUtils.java
index 451bb1d..a3fa31f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/IntentUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/IntentUtils.java
@@ -14,6 +14,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.TransactionTooLargeException;
+import android.support.annotation.Nullable;
 import android.support.v4.app.BundleCompat;
 
 import org.chromium.base.Log;
@@ -312,15 +313,22 @@
         intent.putExtras(bundle);
     }
 
+    /** See {@link #safeStartActivity(Context, Intent, Bundle)}. */
+    public static boolean safeStartActivity(Context context, Intent intent) {
+        return safeStartActivity(context, intent, null);
+    }
+
     /**
      * Catches any failures to start an Activity.
      * @param context Context to use when starting the Activity.
      * @param intent  Intent to fire.
+     * @param bundle  Bundle of launch options.
      * @return Whether or not Android accepted the Intent.
      */
-    public static boolean safeStartActivity(Context context, Intent intent) {
+    public static boolean safeStartActivity(
+            Context context, Intent intent, @Nullable Bundle bundle) {
         try {
-            context.startActivity(intent);
+            context.startActivity(intent, bundle);
             return true;
         } catch (ActivityNotFoundException e) {
             return false;
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 60b3632..8d9dd54c 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -563,6 +563,7 @@
   "java/src/org/chromium/chrome/browser/nfc/BeamController.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamProvider.java",
   "java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
+  "java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java",
   "java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java",
   "java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationBuilder.java",
@@ -1636,6 +1637,7 @@
   "junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java",
   "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java",
   "junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java",
+  "junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeUnitTest.java",
   "junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java",
   "junit/src/org/chromium/chrome/browser/ntp/TitleUtilTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java
index 7c4efe05..5ce7257b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java
@@ -321,8 +321,8 @@
                            .endsWith("Name required"));
         assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX,
                 5).endsWith("More information required"));
-        assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX,
-                6).endsWith("Invalid address"));
+        assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX, 6)
+                           .endsWith("Enter a valid address"));
 
         // Selects the fourth billing addresss that misses recipient name brings up the address
         // editor.
@@ -356,8 +356,8 @@
                            .endsWith("Name required"));
         assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX,
                 5).endsWith("More information required"));
-        assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX,
-                6).endsWith("Invalid address"));
+        assertTrue(getSpinnerTextAtPositionInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX, 6)
+                           .endsWith("Enter a valid address"));
 
         // Selects the fifth billing addresss that misses recipient name brings up the address
         // editor.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
index d546bca6..db9950d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
@@ -204,7 +204,7 @@
 
         assertEquals(4, getNumberOfShippingAddressSuggestions());
         assertTrue(getShippingAddressSuggestionLabel(0).contains("Phone number required"));
-        assertTrue(getShippingAddressSuggestionLabel(1).contains("Invalid address"));
+        assertTrue(getShippingAddressSuggestionLabel(1).contains("Enter a valid address"));
         assertTrue(getShippingAddressSuggestionLabel(2).contains("Name required"));
         assertTrue(getShippingAddressSuggestionLabel(3).contains("More information required"));
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java
index 04578c68..1974aa1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.media.router.ChromeMediaRouter;
+import org.chromium.chrome.browser.media.router.MediaRoute;
 import org.chromium.chrome.browser.media.router.MediaRouteManager;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
@@ -62,4 +63,37 @@
                 .onSinksReceived(
                         eq(UNSUPPORTED_SOURCE), same(provider), eq(new ArrayList<MediaSink>()));
     }
+
+    @Test
+    @Feature({"MediaRouter"})
+    public void testOnSessionClosedNoClientRecord() {
+        ChromeMediaRouter.setAndroidMediaRouterForTest(mock(MediaRouter.class));
+
+        MediaRouteManager mockManager = mock(MediaRouteManager.class);
+        CastMediaRouteProvider provider = CastMediaRouteProvider.create(mockManager);
+
+        CastSession mockSession = mock(CastSession.class);
+        provider.onSessionCreated(mockSession);
+
+        MediaRoute route = new MediaRoute("sink", SUPPORTED_SOURCE, "");
+        provider.addRoute(route, "", -1);
+        provider.onSessionClosed();
+
+        verify(mockManager).onRouteClosed(route.id);
+    }
+
+    @Test
+    @Feature({"MediaRouter"})
+    public void testCloseRouteWithNoSession() {
+        ChromeMediaRouter.setAndroidMediaRouterForTest(mock(MediaRouter.class));
+
+        MediaRouteManager mockManager = mock(MediaRouteManager.class);
+        CastMediaRouteProvider provider = CastMediaRouteProvider.create(mockManager);
+
+        MediaRoute route = new MediaRoute("sink", SUPPORTED_SOURCE, "");
+        provider.addRoute(route, "", -1);
+        provider.closeRoute(route.id);
+
+        verify(mockManager).onRouteClosed(route.id);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java
new file mode 100644
index 0000000..1ecb37c
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java
@@ -0,0 +1,80 @@
+// 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.notifications;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.app.NotificationManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import org.chromium.chrome.test.util.browser.notifications.MockNotificationManagerProxy;
+
+/**
+ * Unit tests for ChannelsInitializer.
+ */
+@RunWith(BlockJUnit4ClassRunner.class)
+public class ChannelsInitializerTest {
+    private ChannelsInitializer mChannelsInitializer;
+    private MockNotificationManagerProxy mMockNotificationManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockNotificationManager = new MockNotificationManagerProxy();
+        mChannelsInitializer = new ChannelsInitializer(mMockNotificationManager);
+    }
+
+    @Test
+    public void testEnsureInitialized_browserChannel() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_BROWSER);
+
+        assertThat(mMockNotificationManager.getChannels().size(), is(1));
+        ChannelsInitializer.Channel channel = mMockNotificationManager.getChannels().get(0);
+        assertThat(channel.mId, is(ChannelsInitializer.CHANNEL_ID_BROWSER));
+        assertThat(
+                channel.mNameResId, is(org.chromium.chrome.R.string.notification_category_browser));
+        assertThat(channel.mImportance, is(NotificationManager.IMPORTANCE_LOW));
+        assertThat(channel.mGroupId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+
+        assertThat(mMockNotificationManager.getNotificationChannelGroups().size(), is(1));
+        ChannelsInitializer.ChannelGroup group =
+                mMockNotificationManager.getNotificationChannelGroups().get(0);
+        assertThat(group.mId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+        assertThat(group.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_group_general));
+    }
+
+    @Test
+    public void testEnsureInitialized_sitesChannel() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_SITES);
+
+        assertThat(mMockNotificationManager.getChannels().size(), is(1));
+
+        ChannelsInitializer.Channel channel = mMockNotificationManager.getChannels().get(0);
+        assertThat(channel.mId, is(ChannelsInitializer.CHANNEL_ID_SITES));
+        assertThat(
+                channel.mNameResId, is(org.chromium.chrome.R.string.notification_category_sites));
+        assertThat(channel.mImportance, is(NotificationManager.IMPORTANCE_DEFAULT));
+        assertThat(channel.mGroupId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+
+        assertThat(mMockNotificationManager.getNotificationChannelGroups().size(), is(1));
+        ChannelsInitializer.ChannelGroup group =
+                mMockNotificationManager.getNotificationChannelGroups().get(0);
+        assertThat(group.mId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+        assertThat(group.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_group_general));
+    }
+
+    @Test
+    public void testEnsureInitialized_multipleCalls() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_SITES);
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_BROWSER);
+        assertThat(mMockNotificationManager.getChannels().size(), is(2));
+    }
+}
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 09b68f6..3c02bc6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10540,36 +10540,6 @@
 
           &lt;p&gt;But you can still configure via the command line.  Please see &lt;code&gt;man <ph name="PRODUCT_BINARY_NAME">$2<ex>google-chrome</ex></ph>&lt;/code&gt; for more information on flags and environment variables.&lt;/p&gt;
         </message>
-        <message name="IDS_ABOUT_SANDBOX_TITLE" desc="Title of HTML page which contains the status of the sandbox.">
-          Sandbox Status
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_SUID_SANDBOX" desc="The name of a type of sandbox used by Chrome on UNIX like systems. The name 'SUID' stands for 'Set User ID', however it's a technical term and may be best left untranslated.">
-          SUID Sandbox
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_NAMESPACE_SANDBOX" desc="The name of a type of sandbox used by Chrome on Linux systems. A namespace is a technical term which refers to set of names for objects which are disjoint from the members of all other namespaces.">
-          Namespace Sandbox
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_PID_NAMESPACES" desc="This a technical term for an attribute of the SUID or namespace sandboxes. PID stands for 'Process ID' but, as a technical term, may be best left untranslated. A namespace is another technical term which refers to set of names for objects which are disjoint from the members of all other namespaces.">
-          PID namespaces
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_NET_NAMESPACES" desc="This a technical term for an attribute of the SUID or namespace sandboxes. A namespace is a technical term which refers to set of names for objects which are disjoint from the members of all other namespaces.">
-          Network namespaces
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX" desc="The name of a type of sandbox used by Chrome on Linux systems. 'Seccomp-BPF' is a technical term which should be left untranslated.">
-          Seccomp-BPF sandbox
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC" desc="The name of a type of sandbox used by Chrome on Linux systems. 'Seccomp-BPF' is a technical term which should be left untranslated. TSYNC is a technical term which should be left untranslated.">
-          Seccomp-BPF sandbox supports TSYNC
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_YAMA_LSM" desc="The name of a Linux security module. It is a technical term that should be left untranslated.">
-          Yama LSM enforcing
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_OK" desc="A message telling the user that their sandbox is sufficient.">
-          You are adequately sandboxed.
-        </message>
-        <message name="IDS_ABOUT_SANDBOX_BAD" desc="A message telling the user that their sandbox is insufficient.">
-          You are not adequately sandboxed!
-        </message>
       </if>
 
       <message name="IDS_IMAGE_FILES" desc="The description of the image file extensions in the select file dialog.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 791278ea..8022a99 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2588,6 +2588,8 @@
       "profiles/profile_statistics_common.h",
       "profiles/profile_statistics_factory.cc",
       "profiles/profile_statistics_factory.h",
+      "signin/force_signin_verifier.cc",
+      "signin/force_signin_verifier.h",
       "signin/signin_global_error.cc",
       "signin/signin_global_error.h",
       "signin/signin_global_error_factory.cc",
@@ -3054,6 +3056,8 @@
       "media/android/remote/remote_media_player_manager.h",
       "media/android/router/media_router_android.cc",
       "media/android/router/media_router_android.h",
+      "media/android/router/media_router_android_bridge.cc",
+      "media/android/router/media_router_android_bridge.h",
       "media/android/router/media_router_dialog_controller_android.cc",
       "media/android/router/media_router_dialog_controller_android.h",
       "media/midi_permission_infobar_delegate_android.cc",
@@ -4383,6 +4387,8 @@
     "net/url_request_mock_util.h",
     "notifications/notification_test_util.cc",
     "notifications/notification_test_util.h",
+    "notifications/stub_notification_display_service.cc",
+    "notifications/stub_notification_display_service.h",
     "notifications/stub_notification_platform_bridge.cc",
     "notifications/stub_notification_platform_bridge.h",
     "policy/test/local_policy_test_server.cc",
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
index 6b5a706..bd15315a 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
@@ -253,7 +253,7 @@
   for (base::ListValue::const_iterator it = list->begin();
        it != list->end(); ++it) {
     const base::DictionaryValue* dict = NULL;
-    if (!*it || !(*it)->GetAsDictionary(&dict)) {
+    if (!it->GetAsDictionary(&dict)) {
       NOTREACHED();
       continue;
     }
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index a339bf73..9ed3315 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -269,7 +269,7 @@
     {"ChromeHttpAuthHandler",
      ChromeHttpAuthHandler::RegisterChromeHttpAuthHandler},
 #if defined(ENABLE_MEDIA_ROUTER)
-    {"ChromeMediaRouter", media_router::MediaRouterAndroid::Register},
+    {"ChromeMediaRouter", media_router::MediaRouterAndroidBridge::Register},
     {"ChromeMediaRouterDialogController",
      media_router::MediaRouterDialogControllerAndroid::Register},
 #endif
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.cc b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
index f10779ff..a13183d 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.cc
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
@@ -97,7 +97,10 @@
     float divider_line_x_offset,
     bool touch_highlight_visible,
     float touch_highlight_x_offset,
-    float touch_highlight_width) {
+    float touch_highlight_width,
+    int bar_handle_resource_id,
+    float bar_handle_offset_y,
+    float bar_padding_bottom) {
   // Round values to avoid pixel gap between layers.
   search_bar_height = floor(search_bar_height);
 
@@ -109,7 +112,7 @@
   OverlayPanelLayer::SetResourceIds(
       search_term_resource_id, panel_shadow_resource_id,
       search_bar_shadow_resource_id, search_provider_icon_resource_id,
-      close_icon_resource_id);
+      close_icon_resource_id, bar_handle_resource_id);
 
   float content_view_top = search_bar_bottom + search_promo_height;
   float should_render_bar_border = search_bar_border_visible
@@ -119,22 +122,12 @@
   // Overlay Panel
   // -----------------------------------------------------------------
   OverlayPanelLayer::SetProperties(
-      dp_to_px,
-      content_layer,
-      content_view_top,
-      search_panel_x,
-      search_panel_y,
-      search_panel_width,
-      search_panel_height,
-      search_bar_margin_side,
-      search_bar_height,
-      search_bar_top,
-      search_term_opacity,
-      should_render_bar_border,
-      search_bar_border_height,
-      search_bar_shadow_visible,
-      search_bar_shadow_opacity,
-      close_icon_opacity);
+      dp_to_px, content_layer, content_view_top, search_panel_x, search_panel_y,
+      search_panel_width, search_panel_height, search_bar_margin_side,
+      search_bar_height, search_bar_top, search_term_opacity,
+      should_render_bar_border, search_bar_border_height,
+      search_bar_shadow_visible, search_bar_shadow_opacity, close_icon_opacity,
+      bar_handle_offset_y, bar_padding_bottom);
 
   bool is_rtl = l10n_util::IsLayoutRtl();
 
@@ -233,17 +226,11 @@
   // ---------------------------------------------------------------------------
   // Search Term, Context and Search Caption
   // ---------------------------------------------------------------------------
-  SetupTextLayer(
-      search_bar_top,
-      search_bar_height,
-      search_text_layer_min_height,
-      search_caption_resource_id,
-      search_caption_visible,
-      search_caption_animation_percentage,
-      search_term_opacity,
-      search_context_resource_id,
-      search_context_opacity,
-      search_term_caption_spacing);
+  SetupTextLayer(bar_content_height_, search_text_layer_min_height,
+                 search_caption_resource_id, search_caption_visible,
+                 search_caption_animation_percentage, search_term_opacity,
+                 search_context_resource_id, search_context_opacity,
+                 search_term_caption_spacing);
 
   // ---------------------------------------------------------------------------
   // Arrow Icon
@@ -262,8 +249,8 @@
   }
 
   // Centers the Arrow Icon vertically in the bar.
-  float arrow_icon_top = search_bar_top + search_bar_height / 2 -
-                         arrow_icon_resource->size().height() / 2;
+  float arrow_icon_top =
+      (bar_content_height_ - arrow_icon_resource->size().height()) / 2;
 
   arrow_icon_->SetUIResourceId(arrow_icon_resource->ui_resource()->id());
   arrow_icon_->SetBounds(arrow_icon_resource->size());
@@ -384,11 +371,11 @@
   // ---------------------------------------------------------------------------
   if (divider_line_visibility_percentage > 0.f) {
     if (divider_line_->parent() != layer_)
-      layer_->AddChild(divider_line_);
+      bar_content_layer_->AddChild(divider_line_);
 
     // The divider line animates in from the bottom.
     float divider_line_y_offset =
-        ((search_bar_height - divider_line_height) / 2) +
+        ((bar_content_height_ - divider_line_height) / 2) +
         (divider_line_height * (1.f - divider_line_visibility_percentage));
     divider_line_->SetPosition(gfx::PointF(divider_line_x_offset,
                                            divider_line_y_offset));
@@ -519,8 +506,7 @@
       gfx::PointF(side_margin, custom_image_y_offset));
 }
 
-void ContextualSearchLayer::SetupTextLayer(float bar_top,
-                                           float bar_height,
+void ContextualSearchLayer::SetupTextLayer(float bar_height,
                                            float search_text_layer_min_height,
                                            int caption_resource_id,
                                            bool caption_visible,
@@ -591,7 +577,7 @@
   float layer_width =
       std::max(main_text->bounds().width(), search_caption_->bounds().width());
 
-  float layer_top = bar_top + (bar_height - layer_height) / 2;
+  float layer_top = (bar_height - layer_height) / 2;
   text_layer_->SetBounds(gfx::Size(layer_width, layer_height));
   text_layer_->SetPosition(gfx::PointF(0.f, layer_top));
   text_layer_->SetMasksToBounds(true);
@@ -767,7 +753,7 @@
 
   // Arrow Icon
   arrow_icon_->SetIsDrawable(true);
-  layer_->AddChild(arrow_icon_);
+  bar_content_layer_->AddChild(arrow_icon_);
 
   // Search Opt Out Promo
   search_promo_container_->SetIsDrawable(true);
@@ -784,7 +770,7 @@
 
   // Icon - holds thumbnail, search provider icon and/or quick action icon
   icon_layer_->SetIsDrawable(true);
-  layer_->AddChild(icon_layer_);
+  bar_content_layer_->AddChild(icon_layer_);
 
   // Search provider icon
   search_provider_icon_layer_->SetIsDrawable(true);
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.h b/chrome/browser/android/compositor/layer/contextual_search_layer.h
index 1c0f1b78..83fc74a 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.h
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.h
@@ -90,7 +90,10 @@
                      float divider_line_x_offset,
                      bool touch_highlight_visible,
                      float touch_highlight_x_offset,
-                     float touch_highlight_width);
+                     float touch_highlight_width,
+                     int bar_handle_resource_id,
+                     float bar_handle_offset_y,
+                     float bar_padding_bottom);
 
   void SetThumbnail(const SkBitmap* thumbnail);
 
@@ -116,8 +119,7 @@
 
   // Sets up |text_layer_|, which contains |bar_text_|, |search_context_| and
   // |search_caption_|.
-  void SetupTextLayer(float search_bar_top,
-                      float search_bar_height,
+  void SetupTextLayer(float search_bar_height,
                       float search_text_layer_min_height,
                       int search_caption_resource_id,
                       bool search_caption_visible,
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
index 42e2a558..8b40970 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
@@ -47,17 +47,18 @@
   text_container_->AddChild(text_layer);
 }
 
-void OverlayPanelLayer::SetResourceIds(
-    int bar_text_resource_id,
-    int panel_shadow_resource_id,
-    int bar_shadow_resource_id,
-    int panel_icon_resource_id,
-    int close_icon_resource_id) {
+void OverlayPanelLayer::SetResourceIds(int bar_text_resource_id,
+                                       int panel_shadow_resource_id,
+                                       int bar_shadow_resource_id,
+                                       int panel_icon_resource_id,
+                                       int close_icon_resource_id,
+                                       int handle_resource_id) {
   bar_text_resource_id_ = bar_text_resource_id;
   panel_shadow_resource_id_ = panel_shadow_resource_id;
   bar_shadow_resource_id_ = bar_shadow_resource_id;
   panel_icon_resource_id_ = panel_icon_resource_id;
   close_icon_resource_id_ = close_icon_resource_id;
+  bar_handle_resource_id_ = handle_resource_id;
 }
 
 void OverlayPanelLayer::SetProperties(
@@ -76,8 +77,9 @@
     float bar_border_height,
     bool bar_shadow_visible,
     float bar_shadow_opacity,
-    float close_icon_opacity) {
-
+    float close_icon_opacity,
+    float bar_handle_offset_y,
+    float bar_padding_bottom) {
   // Grabs required static resources.
   ui::NinePatchResource* panel_shadow_resource =
       ui::NinePatchResource::From(resource_manager_->GetResource(
@@ -119,6 +121,43 @@
   bar_background_->SetPosition(gfx::PointF(0.f, bar_top));
 
   // ---------------------------------------------------------------------------
+  // Bar Handle
+  // ---------------------------------------------------------------------------
+
+  float bar_content_top = bar_top;
+  bar_content_height_ = bar_height - bar_padding_bottom;
+
+  if (bar_handle_resource_id_ != 0) {
+    if (bar_handle_ == nullptr) {
+      bar_handle_ = cc::UIResourceLayer::Create();
+      bar_handle_->SetIsDrawable(true);
+      layer_->AddChild(bar_handle_);
+    }
+
+    // Grab the bar handle resource.
+    ui::Resource* bar_handle_resource = resource_manager_->GetResource(
+        ui::ANDROID_RESOURCE_TYPE_STATIC, bar_handle_resource_id_);
+
+    // Center the handle horizontally.
+    float bar_handle_left =
+        (panel_width - bar_handle_resource->size().width()) / 2;
+    float bar_handle_top = bar_top + bar_handle_offset_y;
+
+    bar_handle_->SetUIResourceId(bar_handle_resource->ui_resource()->id());
+    bar_handle_->SetBounds(bar_handle_resource->size());
+    bar_handle_->SetPosition(gfx::PointF(bar_handle_left, bar_handle_top));
+
+    bar_content_top = bar_handle_top + bar_handle_resource->size().height();
+    bar_content_height_ = bar_height - bar_content_top - bar_padding_bottom;
+  }
+
+  // ---------------------------------------------------------------------------
+  // Bar Content Layer
+  // ---------------------------------------------------------------------------
+  bar_content_layer_->SetBounds(gfx::Size(panel_width, bar_content_height_));
+  bar_content_layer_->SetPosition(gfx::PointF(0.f, bar_content_top));
+
+  // ---------------------------------------------------------------------------
   // Bar Text
   // ---------------------------------------------------------------------------
   ui::Resource* bar_text_resource = resource_manager_->GetResource(
@@ -127,7 +166,7 @@
   if (bar_text_resource) {
     // Centers the text vertically in the Search Bar.
     float bar_padding_top =
-        bar_top + bar_height / 2 - bar_text_resource->size().height() / 2;
+        (bar_content_height_ - bar_text_resource->size().height()) / 2;
     bar_text_->SetUIResourceId(bar_text_resource->ui_resource()->id());
     bar_text_->SetBounds(bar_text_resource->size());
     bar_text_->SetPosition(gfx::PointF(0.f, bar_padding_top));
@@ -154,8 +193,7 @@
     }
 
     // Centers the Icon vertically in the bar.
-    float icon_y = bar_top + bar_height / 2 -
-        icon_layer->bounds().height() / 2;
+    float icon_y = (bar_content_height_ - icon_layer->bounds().height()) / 2;
 
     icon_layer->SetPosition(gfx::PointF(icon_x, icon_y));
   }
@@ -178,7 +216,7 @@
 
   // Centers the Close Icon vertically in the bar.
   float close_icon_top =
-      bar_top + bar_height / 2 - close_icon_resource->size().height() / 2;
+      (bar_content_height_ - close_icon_resource->size().height()) / 2;
 
   close_icon_->SetUIResourceId(close_icon_resource->ui_resource()->id());
   close_icon_->SetBounds(close_icon_resource->size());
@@ -255,7 +293,8 @@
       close_icon_(cc::UIResourceLayer::Create()),
       content_container_(cc::SolidColorLayer::Create()),
       text_container_(cc::Layer::Create()),
-      bar_border_(cc::SolidColorLayer::Create()) {
+      bar_border_(cc::SolidColorLayer::Create()),
+      bar_content_layer_(cc::UIResourceLayer::Create()) {
   layer_->SetMasksToBounds(false);
   layer_->SetIsDrawable(true);
 
@@ -269,10 +308,14 @@
   bar_background_->SetBackgroundColor(kBarBackgroundColor);
   layer_->AddChild(bar_background_);
 
+  // Bar Content Layer
+  bar_content_layer_->SetIsDrawable(true);
+  layer_->AddChild(bar_content_layer_);
+
   // Bar Text
   bar_text_->SetIsDrawable(true);
   AddBarTextLayer(bar_text_);
-  layer_->AddChild(text_container_);
+  bar_content_layer_->AddChild(text_container_);
 
   // Panel Icon
   panel_icon_->SetIsDrawable(true);
@@ -282,7 +325,7 @@
 
   // Close Icon
   close_icon_->SetIsDrawable(true);
-  layer_->AddChild(close_icon_);
+  bar_content_layer_->AddChild(close_icon_);
 
   // Content Container
   content_container_->SetIsDrawable(true);
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.h b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
index 147def7..f842bd5 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.h
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
@@ -28,7 +28,8 @@
                       int panel_shadow_resource_id,
                       int bar_shadow_resource_id,
                       int panel_icon_resource_id,
-                      int close_icon_resource_id);
+                      int close_icon_resource_id,
+                      int handle_resource_id);
 
   void SetProperties(float dp_to_px,
                      const scoped_refptr<cc::Layer>& content_layer,
@@ -45,7 +46,9 @@
                      float bar_border_height,
                      bool bar_shadow_visible,
                      float bar_shadow_opacity,
-                     float close_icon_opacity);
+                     float close_icon_opacity,
+                     float bar_handle_offset_y,
+                     float bar_padding_bottom);
 
   scoped_refptr<cc::Layer> layer() override;
 
@@ -68,12 +71,17 @@
   scoped_refptr<cc::Layer> content_container_;
   scoped_refptr<cc::Layer> text_container_;
   scoped_refptr<cc::SolidColorLayer> bar_border_;
+  scoped_refptr<cc::UIResourceLayer> bar_handle_;
+  scoped_refptr<cc::Layer> bar_content_layer_;
 
   int panel_icon_resource_id_;
   int bar_text_resource_id_;
   int panel_shadow_resource_id_;
   int bar_shadow_resource_id_;
   int close_icon_resource_id_;
+  int bar_handle_resource_id_;
+
+  int bar_content_height_;
 };
 
 }  //  namespace android
diff --git a/chrome/browser/android/compositor/layer/reader_mode_layer.cc b/chrome/browser/android/compositor/layer/reader_mode_layer.cc
index 504986f..f449291 100644
--- a/chrome/browser/android/compositor/layer/reader_mode_layer.cc
+++ b/chrome/browser/android/compositor/layer/reader_mode_layer.cc
@@ -35,22 +35,10 @@
   bar_height = floor(bar_height);
 
   OverlayPanelLayer::SetProperties(
-      dp_to_px,
-      content_layer,
-      bar_height,
-      panel_x,
-      panel_y,
-      panel_width,
-      panel_height,
-      bar_margin_side,
-      bar_height,
-      0.0f,
-      text_opacity,
-      bar_border_visible,
-      bar_border_height,
-      bar_shadow_visible,
-      bar_shadow_opacity,
-      1.0f);
+      dp_to_px, content_layer, bar_height, panel_x, panel_y, panel_width,
+      panel_height, bar_margin_side, bar_height, 0.0f, text_opacity,
+      bar_border_visible, bar_border_height, bar_shadow_visible,
+      bar_shadow_opacity, 1.0f, 0, 0);
 }
 
 ReaderModeLayer::ReaderModeLayer(
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
index 65e1880..2c51567e 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
@@ -122,6 +122,9 @@
     jboolean touch_highlight_visible,
     jfloat touch_highlight_x_offset,
     jfloat touch_highlight_width,
+    jint bar_handle_resource_id,
+    jfloat bar_handle_offset_y,
+    jfloat bar_padding_bottom,
     jobject j_profile) {
   // Load the thumbnail if necessary.
   std::string thumbnail_url =
@@ -178,7 +181,8 @@
       progress_bar_height, progress_bar_opacity, progress_bar_completion,
       divider_line_visibility_percentage, divider_line_width,
       divider_line_height, divider_line_color, divider_line_x_offset,
-      touch_highlight_visible, touch_highlight_x_offset, touch_highlight_width);
+      touch_highlight_visible, touch_highlight_x_offset, touch_highlight_width,
+      bar_handle_resource_id, bar_handle_offset_y, bar_padding_bottom);
 
   // Make the layer visible if it is not already.
   contextual_search_layer_->layer()->SetHideLayerAndSubtree(false);
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
index 8815823..72b21cb 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
@@ -100,6 +100,9 @@
       jboolean touch_highlight_visible,
       jfloat touch_highlight_x_offset,
       jfloat touch_highlight_width,
+      jint bar_handle_resource_id,
+      jfloat bar_handle_offset_y,
+      jfloat bar_padding_bottom,
       jobject j_profile);
 
   // Inherited from BitmapFetcherDelegate
diff --git a/chrome/browser/android/compositor/scene_layer/reader_mode_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/reader_mode_scene_layer.cc
index 5ae24a23..58c738b3 100644
--- a/chrome/browser/android/compositor/scene_layer/reader_mode_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/reader_mode_scene_layer.cc
@@ -54,13 +54,9 @@
     jint bar_shadow_resource_id,
     jint panel_icon_resource_id,
     jint close_icon_resource_id) {
-
   reader_mode_layer_->SetResourceIds(
-      text_resource_id,
-      bar_background_resource_id,
-      bar_shadow_resource_id,
-      panel_icon_resource_id,
-      close_icon_resource_id);
+      text_resource_id, bar_background_resource_id, bar_shadow_resource_id,
+      panel_icon_resource_id, close_icon_resource_id, 0);
 }
 
 void ReaderModeSceneLayer::Update(
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner.cc b/chrome/browser/android/offline_pages/background_loader_offliner.cc
index 90a5f96b..603263f8 100644
--- a/chrome/browser/android/offline_pages/background_loader_offliner.cc
+++ b/chrome/browser/android/offline_pages/background_loader_offliner.cc
@@ -255,32 +255,6 @@
     RecordErrorCauseUMA(pending_request_->client_id(),
                         navigation_handle->GetNetErrorCode());
     switch (navigation_handle->GetNetErrorCode()) {
-      case net::ERR_ACCESS_DENIED:
-      case net::ERR_ADDRESS_INVALID:
-      case net::ERR_ADDRESS_UNREACHABLE:
-      case net::ERR_CERT_COMMON_NAME_INVALID:
-      case net::ERR_CERT_AUTHORITY_INVALID:
-      case net::ERR_CERT_CONTAINS_ERRORS:
-      case net::ERR_CERT_INVALID:
-      case net::ERR_CONNECTION_FAILED:
-      case net::ERR_DISALLOWED_URL_SCHEME:
-      case net::ERR_DNS_SERVER_FAILED:
-      case net::ERR_FILE_NOT_FOUND:
-      case net::ERR_FILE_PATH_TOO_LONG:
-      case net::ERR_FILE_TOO_BIG:
-      case net::ERR_FILE_VIRUS_INFECTED:
-      case net::ERR_INVALID_HANDLE:
-      case net::ERR_INVALID_RESPONSE:
-      case net::ERR_INVALID_URL:
-      case net::ERR_MSG_TOO_BIG:
-      case net::ERR_NAME_NOT_RESOLVED:
-      case net::ERR_NAME_RESOLUTION_FAILED:
-      case net::ERR_SSL_PROTOCOL_ERROR:
-      case net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED:
-      case net::ERR_SSL_SERVER_CERT_BAD_FORMAT:
-      case net::ERR_UNKNOWN_URL_SCHEME:
-        page_load_state_ = NONRETRIABLE;
-        break;
       case net::ERR_INTERNET_DISCONNECTED:
         page_load_state_ = DELAY_RETRY;
         break;
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc b/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
index efe87a84..da2efcd4 100644
--- a/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
+++ b/chrome/browser/android/offline_pages/background_loader_offliner_unittest.cc
@@ -437,7 +437,7 @@
   PumpLoop();
 
   EXPECT_TRUE(completion_callback_called());
-  EXPECT_EQ(Offliner::RequestStatus::LOADING_FAILED_NO_RETRY, request_status());
+  EXPECT_EQ(Offliner::RequestStatus::LOADING_FAILED, request_status());
 }
 
 TEST_F(BackgroundLoaderOfflinerTest, NoNextOnInternetDisconnected) {
diff --git a/chrome/browser/android/vr_shell/gltf_parser.cc b/chrome/browser/android/vr_shell/gltf_parser.cc
index b8f5bd7..6dbde77 100644
--- a/chrome/browser/android/vr_shell/gltf_parser.cc
+++ b/chrome/browser/android/vr_shell/gltf_parser.cc
@@ -206,7 +206,7 @@
     if (mesh_dict->GetList("primitives", &list)) {
       for (const auto& primitive_value : *list) {
         const base::DictionaryValue* primitive_dict;
-        if (!primitive_value->GetAsDictionary(&primitive_dict))
+        if (!primitive_value.GetAsDictionary(&primitive_dict))
           return false;
 
         auto primitive = ProcessPrimitive(*primitive_dict);
@@ -261,7 +261,7 @@
     if (node_dict->GetList("meshes", &list)) {
       std::string mesh_key;
       for (const auto& mesh_value : *list) {
-        if (!mesh_value->GetAsString(&mesh_key))
+        if (!mesh_value.GetAsString(&mesh_key))
           return false;
         auto mesh_it = mesh_ids_.find(mesh_key);
         if (mesh_it == mesh_ids_.end())
@@ -284,7 +284,7 @@
     if (node_dict->GetList("children", &list)) {
       std::string node_key;
       for (const auto& mesh_value : *list) {
-        if (!mesh_value->GetAsString(&node_key))
+        if (!mesh_value.GetAsString(&node_key))
           return false;
         auto node_it = nodes.find(node_key);
         if (node_it == nodes.end())
@@ -308,7 +308,7 @@
     if (scene_dict->GetList("nodes", &list)) {
       std::string node_key;
       for (const auto& node_value : *list) {
-        if (!node_value->GetAsString(&node_key))
+        if (!node_value.GetAsString(&node_key))
           return false;
         auto node_it = node_ids_.find(node_key);
         if (node_it == node_ids_.end())
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index d1c387ae..e6e1620 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -290,7 +290,7 @@
                              const base::TimeTicks& time) {
   for (auto& item : *commands) {
     base::DictionaryValue* dict;
-    CHECK(item->GetAsDictionary(&dict));
+    CHECK(item.GetAsDictionary(&dict));
 
     Command type;
     base::DictionaryValue* data;
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 57aedb9f..4074995 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -1776,7 +1776,14 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest, SimpleCrossSiteFill) {
+#if defined(OS_WIN)
+// Flaky on Windows 7 in debug build. http://crbug.com/710436
+#define MAYBE_SimpleCrossSiteFill DISABLED_SimpleCrossSiteFill
+#else
+#define MAYBE_SimpleCrossSiteFill SimpleCrossSiteFill
+#endif
+IN_PROC_BROWSER_TEST_F(AutofillInteractiveIsolationTest,
+                       MAYBE_SimpleCrossSiteFill) {
   // Ensure that |embedded_test_server()| serves both domains used below.
   host_resolver()->AddRule("*", "127.0.0.1");
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index bf9c53b..7b32099 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -666,6 +666,10 @@
       <if expr="not is_android and not is_ios">
         <include name="IDR_SSL_ERROR_ASSISTANT_PB" file="${root_gen_dir}/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.pb" use_base_dir="false" type="BINDATA" />
       </if>
+      <if expr="is_android or is_linux">
+        <include name="IDR_SANDBOX_INTERNALS_HTML" file="resources\sandbox_internals\sandbox_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_SANDBOX_INTERNALS_JS" file="resources\sandbox_internals\sandbox_internals.js" type="BINDATA" compress="gzip" />
+      </if>
     </includes>
   </release>
 </grit>
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl.cc b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
index 68de911c..dee3c1a 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
@@ -48,18 +48,6 @@
 
 namespace {
 
-template <typename T>
-void IgnoreArgumentHelper(const base::Closure& callback, T unused_argument) {
-  callback.Run();
-}
-
-// Another convenience method to turn a callback without arguments into one that
-// accepts (and ignores) a single argument.
-template <typename T>
-base::Callback<void(T)> IgnoreArgument(const base::Closure& callback) {
-  return base::Bind(&IgnoreArgumentHelper<T>, callback);
-}
-
 // Returns whether |origin| matches |origin_type_mask| given the special
 // storage |policy|; and if |predicate| is not null, then also whether
 // it matches |predicate|. If |origin_type_mask| contains embedder-specific
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.cc b/chrome/browser/chrome_browser_field_trials_desktop.cc
index 8956ef56..245697d 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.cc
+++ b/chrome/browser/chrome_browser_field_trials_desktop.cc
@@ -164,19 +164,18 @@
                                                 &version_number, &special_build,
                                                 &channel_name);
 
-    base::debug::ActivityUserData& global_data = global_tracker->global_data();
-    global_data.SetString(browser_watcher::kStabilityProduct, product_name);
-    global_data.SetString(browser_watcher::kStabilityVersion, version_number);
-    global_data.SetString(browser_watcher::kStabilityChannel, channel_name);
-    global_data.SetString(browser_watcher::kStabilitySpecialBuild,
-                          special_build);
+    base::debug::ActivityUserData& proc_data = global_tracker->process_data();
+    proc_data.SetString(browser_watcher::kStabilityProduct, product_name);
+    proc_data.SetString(browser_watcher::kStabilityVersion, version_number);
+    proc_data.SetString(browser_watcher::kStabilityChannel, channel_name);
+    proc_data.SetString(browser_watcher::kStabilitySpecialBuild, special_build);
 #if defined(ARCH_CPU_X86)
-    global_data.SetString(browser_watcher::kStabilityPlatform, "Win32");
+    proc_data.SetString(browser_watcher::kStabilityPlatform, "Win32");
 #elif defined(ARCH_CPU_X86_64)
-    global_data.SetString(browser_watcher::kStabilityPlatform, "Win64");
+    proc_data.SetString(browser_watcher::kStabilityPlatform, "Win64");
 #endif
-    global_data.SetInt(browser_watcher::kStabilityStartTimestamp,
-                       base::Time::Now().ToInternalValue());
+    proc_data.SetInt(browser_watcher::kStabilityStartTimestamp,
+                     base::Time::Now().ToInternalValue());
 
     // Record information about chrome's module. We want this to be done early.
     RecordChromeModuleInfo(global_tracker);
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index df7b1cc..612d16b 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -855,7 +855,7 @@
       std::vector<base::StringPiece> disabled_features;
       base::StringPiece disabled_feature;
       for (const auto& item : *override_disabled_feature_list) {
-        if (item->GetAsString(&disabled_feature)) {
+        if (item.GetAsString(&disabled_feature)) {
           disabled_features.push_back(disabled_feature);
         }
       }
@@ -874,7 +874,7 @@
       std::vector<base::StringPiece> disabled_tokens;
       base::StringPiece disabled_token;
       for (const auto& item : *disabled_token_list) {
-        if (item->GetAsString(&disabled_token)) {
+        if (item.GetAsString(&disabled_token)) {
           disabled_tokens.push_back(disabled_token);
         }
       }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index bf079f4..260c28e2 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1790,7 +1790,7 @@
         for (base::ListValue::const_iterator it = switches->begin();
              it != switches->end(); ++it) {
           std::string switch_to_enable;
-          if ((*it)->GetAsString(&switch_to_enable))
+          if (it->GetAsString(&switch_to_enable))
             command_line->AppendSwitch(switch_to_enable);
         }
       }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index ce91c78..e549841b 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -176,8 +176,6 @@
     "app_mode/app_launch_utils.h",
     "app_mode/app_session.cc",
     "app_mode/app_session.h",
-    "app_mode/arc/arc_kiosk_app_data.cc",
-    "app_mode/arc/arc_kiosk_app_data.h",
     "app_mode/arc/arc_kiosk_app_launcher.cc",
     "app_mode/arc/arc_kiosk_app_launcher.h",
     "app_mode/arc/arc_kiosk_app_manager.cc",
@@ -190,13 +188,9 @@
     "app_mode/certificate_manager_dialog.h",
     "app_mode/kiosk_app_data.cc",
     "app_mode/kiosk_app_data.h",
-    "app_mode/kiosk_app_data_base.cc",
-    "app_mode/kiosk_app_data_base.h",
     "app_mode/kiosk_app_data_delegate.h",
     "app_mode/kiosk_app_external_loader.cc",
     "app_mode/kiosk_app_external_loader.h",
-    "app_mode/kiosk_app_icon_loader.cc",
-    "app_mode/kiosk_app_icon_loader.h",
     "app_mode/kiosk_app_launch_error.cc",
     "app_mode/kiosk_app_launch_error.h",
     "app_mode/kiosk_app_manager.cc",
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.cc
deleted file mode 100644
index 0fd43e7..0000000
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h"
-
-#include <utility>
-
-#include "base/path_service.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
-#include "chrome/common/chrome_paths.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace chromeos {
-
-namespace {
-
-constexpr char kIconCacheDir[] = "arc-kiosk/icon";
-
-}  // namespace
-
-ArcKioskAppData::ArcKioskAppData(const std::string& app_id,
-                                 const AccountId& account_id,
-                                 const std::string& name)
-    : KioskAppDataBase(ArcKioskAppManager::kArcKioskDictionaryName,
-                       app_id,
-                       account_id) {
-  name_ = name;
-}
-
-ArcKioskAppData::~ArcKioskAppData() = default;
-
-bool ArcKioskAppData::operator==(const std::string& other_app_id) const {
-  return app_id() == other_app_id;
-}
-
-bool ArcKioskAppData::LoadFromCache() {
-  PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(dictionary_name());
-
-  return LoadFromDictionary(*dict);
-}
-
-void ArcKioskAppData::SetCache(const std::string& name,
-                               const gfx::ImageSkia& icon) {
-  DCHECK(!name.empty());
-  DCHECK(!icon.isNull());
-  name_ = name;
-  icon_ = icon;
-
-  base::FilePath user_data_dir;
-  if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
-    LOG(ERROR) << "Failed to get user directory.";
-    return;
-  }
-  base::FilePath cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
-
-  SaveIcon(*icon_.bitmap(), cache_dir);
-
-  PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
-
-  SaveToDictionary(dict_update);
-}
-
-void ArcKioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  kiosk_app_icon_loader_.release();
-  icon_ = icon;
-}
-
-void ArcKioskAppData::OnIconLoadFailure() {
-  kiosk_app_icon_loader_.release();
-  LOG(ERROR) << "Icon Load Failure";
-  // Do nothing
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h
deleted file mode 100644
index 428af43c..0000000
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_ARC_ARC_KIOSK_APP_DATA_H_
-#define CHROME_BROWSER_CHROMEOS_APP_MODE_ARC_ARC_KIOSK_APP_DATA_H_
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_app_data_base.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace chromeos {
-
-// Fetches from Android side and caches ARC kiosk app data such as name and
-// icon.
-class ArcKioskAppData : public KioskAppDataBase {
- public:
-  ArcKioskAppData(const std::string& app_id,
-                  const AccountId& account_id,
-                  const std::string& name);
-  ~ArcKioskAppData() override;
-
-  // TODO(poromov@): Use appropriate app id http://crbug.com/665904
-  // Currently app_id is always package_name.
-  bool operator==(const std::string& other_app_id) const;
-
-  // Loads the locally cached data. Return false if there is none.
-  bool LoadFromCache();
-
-  // Sets the cached data.
-  void SetCache(const std::string& name, const gfx::ImageSkia& icon);
-
-  // Callbacks for KioskAppIconLoader.
-  void OnIconLoadSuccess(const gfx::ImageSkia& icon) override;
-  void OnIconLoadFailure() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ArcKioskAppData);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_APP_MODE_ARC_ARC_KIOSK_APP_DATA_H_
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.cc
index 6a4426e..f265c5c 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.cc
@@ -12,7 +12,6 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -79,7 +78,7 @@
   for (base::ListValue::const_iterator it = list->begin(); it != list->end();
        ++it) {
     std::string entry;
-    if (!(*it)->GetAsString(&entry)) {
+    if (!it->GetAsString(&entry)) {
       LOG(ERROR) << "List of cryptohome ids is broken";
       continue;
     }
@@ -97,11 +96,7 @@
 }  // namespace
 
 // static
-const char ArcKioskAppManager::kArcKioskDictionaryName[] = "arc-kiosk";
-
-// static
 void ArcKioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(kArcKioskDictionaryName);
   registry->RegisterListPref(kArcKioskUsersToRemove);
 }
 
@@ -113,6 +108,22 @@
       base::Bind(&PerformDelayedCryptohomeRemovals));
 }
 
+ArcKioskAppManager::ArcKioskApp::ArcKioskApp(const ArcKioskApp& other) =
+    default;
+
+ArcKioskAppManager::ArcKioskApp::ArcKioskApp(
+    const policy::ArcKioskAppBasicInfo& app_info,
+    const AccountId& account_id,
+    const std::string& name)
+    : app_info_(app_info), account_id_(account_id), name_(name) {}
+
+ArcKioskAppManager::ArcKioskApp::~ArcKioskApp() = default;
+
+bool ArcKioskAppManager::ArcKioskApp::operator==(
+    const policy::ArcKioskAppBasicInfo& app_info) const {
+  return this->app_info_ == app_info;
+}
+
 // static
 ArcKioskAppManager* ArcKioskAppManager::Get() {
   return g_arc_kiosk_app_manager;
@@ -143,33 +154,14 @@
   return auto_launch_account_id_;
 }
 
-const ArcKioskAppData* ArcKioskAppManager::GetAppByAccountId(
+const ArcKioskAppManager::ArcKioskApp* ArcKioskAppManager::GetAppByAccountId(
     const AccountId& account_id) {
-  for (auto& app : apps_) {
-    if (app->account_id() == account_id)
-      return app.get();
-  }
+  for (auto& app : GetAllApps())
+    if (app.account_id() == account_id)
+      return &app;
   return nullptr;
 }
 
-void ArcKioskAppManager::GetAllApps(Apps* apps) const {
-  apps->clear();
-  apps->reserve(apps_.size());
-  for (auto& app : apps_)
-    apps->push_back(app.get());
-}
-
-void ArcKioskAppManager::UpdateNameAndIcon(const std::string& app_id,
-                                           const std::string& name,
-                                           const gfx::ImageSkia& icon) {
-  for (auto& app : apps_) {
-    if (app->app_id() == app_id) {
-      app->SetCache(name, icon);
-      return;
-    }
-  }
-}
-
 void ArcKioskAppManager::AddObserver(ArcKioskAppManagerObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -189,10 +181,8 @@
 
   // Store current apps. We will compare old and new apps to determine which
   // apps are new, and which were deleted.
-  std::map<std::string, std::unique_ptr<ArcKioskAppData>> old_apps;
-  for (auto& app : apps_)
-    old_apps[app->app_id()] = std::move(app);
-  apps_.clear();
+  ArcKioskApps old_apps(std::move(apps_));
+
   auto_launch_account_id_.clear();
   auto_launched_with_zero_delay_ = false;
   std::string auto_login_account_id_from_settings;
@@ -216,19 +206,18 @@
       auto_launched_with_zero_delay_ = auto_launch_delay == 0;
     }
 
-    // Apps are keyed by package name. http://crbug.com/665904
-    auto old_it = old_apps.find(account.arc_kiosk_app_info.package_name());
+    auto old_it =
+        std::find(old_apps.begin(), old_apps.end(), account.arc_kiosk_app_info);
     if (old_it != old_apps.end()) {
-      apps_.push_back(std::move(old_it->second));
+      apps_.push_back(std::move(*old_it));
       old_apps.erase(old_it);
     } else {
       // Use package name when display name is not specified.
       std::string name = account.arc_kiosk_app_info.package_name();
       if (!account.arc_kiosk_app_info.display_name().empty())
         name = account.arc_kiosk_app_info.display_name();
-      apps_.push_back(base::MakeUnique<ArcKioskAppData>(
-          account.arc_kiosk_app_info.package_name(), account_id, name));
-      apps_.back()->LoadFromCache();
+      apps_.push_back(
+          ArcKioskApp(account.arc_kiosk_app_info, account_id, name));
     }
     CancelDelayedCryptohomeRemoval(cryptohome::Identification(account_id));
   }
@@ -241,7 +230,7 @@
 }
 
 void ArcKioskAppManager::ClearRemovedApps(
-    const std::map<std::string, std::unique_ptr<ArcKioskAppData>>& old_apps) {
+    const std::vector<ArcKioskApp>& old_apps) {
   // Check if currently active user must be deleted.
   bool active_user_to_be_deleted = false;
   const user_manager::User* active_user =
@@ -249,7 +238,7 @@
   if (active_user) {
     const AccountId active_account_id = active_user->GetAccountId();
     for (const auto& it : old_apps) {
-      if (it.second->account_id() == active_account_id) {
+      if (it.account_id() == active_account_id) {
         active_user_to_be_deleted = true;
         break;
       }
@@ -258,8 +247,7 @@
 
   // Remove cryptohome
   for (auto& entry : old_apps) {
-    entry.second->ClearCache();
-    const cryptohome::Identification cryptohome_id(entry.second->account_id());
+    const cryptohome::Identification cryptohome_id(entry.account_id());
     if (active_user_to_be_deleted) {
       // Schedule cryptohome removal after active user logout.
       ScheduleDelayedCryptohomeRemoval(cryptohome_id);
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h
index 9023959..2243e92 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "components/signin/core/account_id/account_id.h"
@@ -26,7 +25,30 @@
 // creates a user in whose context the app then runs.
 class ArcKioskAppManager {
  public:
-  using Apps = std::vector<ArcKioskAppData*>;
+  // Struct to hold full info about ARC Kiosk app. In future
+  // this structure may contain many extra fields, e.g. icon.
+  class ArcKioskApp {
+   public:
+    ArcKioskApp(const policy::ArcKioskAppBasicInfo& app_info,
+                const AccountId& account_id,
+                const std::string& name);
+    ArcKioskApp(const ArcKioskApp& other);
+    ~ArcKioskApp();
+
+    bool operator==(const std::string& app_id) const;
+    bool operator==(const policy::ArcKioskAppBasicInfo& app_info) const;
+
+    const policy::ArcKioskAppBasicInfo& app_info() const { return app_info_; }
+    const AccountId& account_id() const { return account_id_; }
+    const std::string& name() const { return name_; }
+
+   private:
+    policy::ArcKioskAppBasicInfo app_info_;
+    AccountId account_id_;
+    std::string name_;
+  };
+
+  using ArcKioskApps = std::vector<ArcKioskApp>;
 
   class ArcKioskAppManagerObserver {
    public:
@@ -36,8 +58,6 @@
     virtual ~ArcKioskAppManagerObserver() = default;
   };
 
-  static const char kArcKioskDictionaryName[];
-
   static ArcKioskAppManager* Get();
 
   ArcKioskAppManager();
@@ -54,13 +74,9 @@
   const AccountId& GetAutoLaunchAccountId() const;
 
   // Returns app that should be started for given account id.
-  const ArcKioskAppData* GetAppByAccountId(const AccountId& account_id);
+  const ArcKioskApp* GetAppByAccountId(const AccountId& account_id);
 
-  void GetAllApps(Apps* apps) const;
-
-  void UpdateNameAndIcon(const std::string& app_id,
-                         const std::string& name,
-                         const gfx::ImageSkia& icon);
+  const ArcKioskApps& GetAllApps() const { return apps_; }
 
   void AddObserver(ArcKioskAppManagerObserver* observer);
   void RemoveObserver(ArcKioskAppManagerObserver* observer);
@@ -75,10 +91,9 @@
 
   // Removes cryptohomes of the removed apps. Terminates the session if
   // a removed app is running.
-  void ClearRemovedApps(
-      const std::map<std::string, std::unique_ptr<ArcKioskAppData>>& old_apps);
+  void ClearRemovedApps(const ArcKioskApps& old_apps);
 
-  std::vector<std::unique_ptr<ArcKioskAppData>> apps_;
+  ArcKioskApps apps_;
   AccountId auto_launch_account_id_;
   bool auto_launched_with_zero_delay_ = false;
   base::ObserverList<ArcKioskAppManagerObserver, true> observers_;
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
index 249147d..1c33160 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
@@ -158,13 +158,12 @@
     waiter.Wait();
     EXPECT_TRUE(waiter.was_notified());
 
-    ArcKioskAppManager::Apps apps;
-    manager()->GetAllApps(&apps);
+    ArcKioskAppManager::ArcKioskApps apps = manager()->GetAllApps();
     ASSERT_EQ(2u, apps.size());
-    ASSERT_EQ(app1.package_name(), apps[0]->app_id());
-    ASSERT_EQ(app2.package_name(), apps[1]->app_id());
-    ASSERT_EQ(app1.package_name(), apps[0]->name());
-    ASSERT_EQ(app2.display_name(), apps[1]->name());
+    ASSERT_EQ(app1, apps[0].app_info());
+    ASSERT_EQ(app2, apps[1].app_info());
+    ASSERT_EQ(app1.package_name(), apps[0].name());
+    ASSERT_EQ(app2.display_name(), apps[1].name());
     EXPECT_FALSE(manager()->GetAutoLaunchAccountId().is_valid());
     EXPECT_FALSE(manager()->current_app_was_auto_launched_with_zero_delay());
   }
@@ -179,16 +178,15 @@
     EXPECT_TRUE(waiter.was_notified());
 
     EXPECT_TRUE(manager()->GetAutoLaunchAccountId().is_valid());
+    ASSERT_EQ(2u, manager()->GetAllApps().size());
 
-    ArcKioskAppManager::Apps apps;
-    manager()->GetAllApps(&apps);
-    ASSERT_EQ(2u, apps.size());
-    ASSERT_EQ(app1.package_name(), apps[0]->app_id());
-    ASSERT_EQ(app2.package_name(), apps[1]->app_id());
-    ASSERT_EQ(app1.package_name(), apps[0]->name());
-    ASSERT_EQ(app2.display_name(), apps[1]->name());
+    ArcKioskAppManager::ArcKioskApps apps = manager()->GetAllApps();
+    ASSERT_EQ(app1, apps[0].app_info());
+    ASSERT_EQ(app2, apps[1].app_info());
+    ASSERT_EQ(app1.package_name(), apps[0].name());
+    ASSERT_EQ(app2.display_name(), apps[1].name());
     EXPECT_TRUE(manager()->GetAutoLaunchAccountId().is_valid());
-    ASSERT_EQ(apps[1]->account_id(), manager()->GetAutoLaunchAccountId());
+    ASSERT_EQ(apps[1].account_id(), manager()->GetAutoLaunchAccountId());
     EXPECT_TRUE(manager()->current_app_was_auto_launched_with_zero_delay());
   }
 
@@ -203,13 +201,12 @@
     waiter.Wait();
     EXPECT_TRUE(waiter.was_notified());
 
-    ArcKioskAppManager::Apps apps;
-    manager()->GetAllApps(&apps);
+    ArcKioskAppManager::ArcKioskApps apps = manager()->GetAllApps();
     ASSERT_EQ(2u, apps.size());
-    ASSERT_EQ(app1.package_name(), apps[0]->app_id());
-    ASSERT_EQ(app3.package_name(), apps[1]->app_id());
-    ASSERT_EQ(app1.package_name(), apps[0]->name());
-    ASSERT_EQ(app3.package_name(), apps[1]->name());
+    ASSERT_EQ(app1, apps[0].app_info());
+    ASSERT_EQ(app3, apps[1].app_info());
+    ASSERT_EQ(app1.package_name(), apps[0].name());
+    ASSERT_EQ(app3.package_name(), apps[1].name());
     // Auto launch app must be reset.
     EXPECT_FALSE(manager()->GetAutoLaunchAccountId().is_valid());
     EXPECT_FALSE(manager()->current_app_was_auto_launched_with_zero_delay());
@@ -223,9 +220,7 @@
     waiter.Wait();
     EXPECT_TRUE(waiter.was_notified());
 
-    ArcKioskAppManager::Apps apps;
-    manager()->GetAllApps(&apps);
-    ASSERT_EQ(0u, apps.size());
+    ASSERT_EQ(0u, manager()->GetAllApps().size());
     EXPECT_FALSE(manager()->GetAutoLaunchAccountId().is_valid());
   }
 }
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
index 71f1781..d505ec2 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
@@ -12,10 +12,6 @@
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "ui/app_list/app_list_constants.h"
-#include "ui/base/layout.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification_blocker.h"
 
@@ -138,16 +134,6 @@
     delegate_->OnAppWindowLaunched();
 }
 
-void ArcKioskAppService::OnIconUpdated(ArcAppIcon* icon) {
-  DCHECK_EQ(icon, app_icon_.get());
-  if (icon->image_skia().isNull()) {
-    app_icon_.release();
-    return;
-  }
-  app_manager_->UpdateNameAndIcon(app_info_->package_name, app_info_->name,
-                                  app_icon_->image_skia());
-}
-
 ArcKioskAppService::ArcKioskAppService(Profile* profile) : profile_(profile) {
   ArcAppListPrefs::Get(profile_)->AddObserver(this);
   app_manager_ = ArcKioskAppManager::Get();
@@ -163,17 +149,6 @@
   maintenance_timeout_timer_.Stop();
 }
 
-void ArcKioskAppService::RequestNameAndIconUpdate() {
-  // Request only once when app_icon_ is not initialized.
-  if (!app_info_ || !app_info_->ready || app_icon_)
-    return;
-  app_icon_ = base::MakeUnique<ArcAppIcon>(profile_, app_id_,
-                                           app_list::kGridIconDimension, this);
-  app_icon_->LoadForScaleFactor(ui::GetSupportedScaleFactor(
-      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor()));
-  // Name and icon are updated when icon is loaded in OnIconUpdated()
-}
-
 void ArcKioskAppService::PreconditionsChanged() {
   VLOG(2) << "Preconditions for kiosk app changed";
   app_id_ = GetAppId();
@@ -197,16 +172,17 @@
     VLOG(2) << "Kiosk app should be closed";
     arc::CloseTask(task_id_);
   }
-  RequestNameAndIconUpdate();
 }
 
 std::string ArcKioskAppService::GetAppId() {
   AccountId account_id = multi_user_util::GetAccountIdFromProfile(profile_);
-  const ArcKioskAppData* app = app_manager_->GetAppByAccountId(account_id);
+  const ArcKioskAppManager::ArcKioskApp* app =
+      app_manager_->GetAppByAccountId(account_id);
   if (!app)
     return std::string();
   std::unordered_set<std::string> app_ids =
-      ArcAppListPrefs::Get(profile_)->GetAppsForPackage(app->app_id());
+      ArcAppListPrefs::Get(profile_)->GetAppsForPackage(
+          app->app_info().package_name());
   if (app_ids.empty())
     return std::string();
   // TODO(poromov@): Choose appropriate app id to launch. See
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
index a737022..ac3bf51 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
@@ -9,7 +9,6 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "components/arc/kiosk/arc_kiosk_bridge.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -36,8 +35,7 @@
       public ArcAppListPrefs::Observer,
       public ArcKioskAppManager::ArcKioskAppManagerObserver,
       public arc::ArcKioskBridge::Delegate,
-      public ArcKioskAppLauncher::Delegate,
-      public ArcAppIcon::Observer {
+      public ArcKioskAppLauncher::Delegate {
  public:
   class Delegate {
    public:
@@ -81,9 +79,6 @@
   // ArcKioskAppLauncher::Delegate overrides
   void OnAppWindowLaunched() override;
 
-  // ArcAppIcon::Observer overrides
-  void OnIconUpdated(ArcAppIcon* icon) override;
-
  private:
   explicit ArcKioskAppService(Profile* profile);
   ~ArcKioskAppService() override;
@@ -91,8 +86,6 @@
   std::string GetAppId();
   // Called when app should be started or stopped.
   void PreconditionsChanged();
-  // Updates local cache with proper name and icon.
-  void RequestNameAndIconUpdate();
 
   Profile* const profile_;
   bool maintenance_session_running_ = false;
@@ -100,7 +93,6 @@
   ArcKioskAppManager* app_manager_;
   std::string app_id_;
   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info_;
-  std::unique_ptr<ArcAppIcon> app_icon_;
   int32_t task_id_ = -1;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
   // Keeps track whether the app is already launched
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
index 8d9d1a876..12b742c 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/webstore_data_fetcher.h"
 #include "chrome/browser/extensions/webstore_install_helper.h"
+#include "chrome/browser/image_decoder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -44,10 +45,29 @@
 namespace {
 
 // Keys for local state data. See sample layout in KioskAppManager.
-constexpr char kKeyRequiredPlatformVersion[] = "required_platform_version";
+const char kKeyName[] = "name";
+const char kKeyIcon[] = "icon";
+const char kKeyRequiredPlatformVersion[] = "required_platform_version";
 
-constexpr char kInvalidWebstoreResponseError[] =
-    "Invalid Chrome Web Store reponse";
+const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
+
+// Icon file extension.
+const char kIconFileExtension[] = ".png";
+
+// Save |raw_icon| for given |app_id|.
+void SaveIconToLocalOnBlockingPool(
+    const base::FilePath& icon_path,
+    scoped_refptr<base::RefCountedString> raw_icon) {
+  DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+  base::FilePath dir = icon_path.DirName();
+  if (!base::PathExists(dir))
+    CHECK(base::CreateDirectory(dir));
+
+  CHECK_EQ(static_cast<int>(raw_icon->size()),
+           base::WriteFile(icon_path,
+                           raw_icon->data().c_str(), raw_icon->size()));
+}
 
 // Returns true for valid kiosk app manifest.
 bool IsValidKioskAppManifest(const extensions::Manifest& manifest) {
@@ -183,6 +203,119 @@
 };
 
 ////////////////////////////////////////////////////////////////////////////////
+// KioskAppData::IconLoader
+// Loads locally stored icon data and decode it.
+
+class KioskAppData::IconLoader {
+ public:
+  enum LoadResult {
+    SUCCESS,
+    FAILED_TO_LOAD,
+    FAILED_TO_DECODE,
+  };
+
+  IconLoader(const base::WeakPtr<KioskAppData>& client,
+             const base::FilePath& icon_path)
+      : client_(client),
+        icon_path_(icon_path),
+        load_result_(SUCCESS) {}
+
+  void Start() {
+    base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
+    base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
+    task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
+        token,
+        base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&IconLoader::LoadOnBlockingPool,
+                                      base::Unretained(this)));
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<IconLoader>;
+
+  ~IconLoader() {}
+
+  class IconImageRequest : public ImageDecoder::ImageRequest {
+   public:
+    IconImageRequest(
+        const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+        IconLoader* icon_loader)
+        : ImageRequest(task_runner), icon_loader_(icon_loader) {}
+
+    void OnImageDecoded(const SkBitmap& decoded_image) override {
+      icon_loader_->icon_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
+      icon_loader_->icon_.MakeThreadSafe();
+      icon_loader_->ReportResultOnBlockingPool(SUCCESS);
+      delete this;
+    }
+
+    void OnDecodeImageFailed() override {
+      icon_loader_->ReportResultOnBlockingPool(FAILED_TO_DECODE);
+      delete this;
+    }
+
+   private:
+    ~IconImageRequest() override {}
+    IconLoader* icon_loader_;
+  };
+
+  // Loads the icon from locally stored |icon_path_| on the blocking pool
+  void LoadOnBlockingPool() {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+
+    std::string data;
+    if (!base::ReadFileToString(base::FilePath(icon_path_), &data)) {
+      ReportResultOnBlockingPool(FAILED_TO_LOAD);
+      return;
+    }
+    raw_icon_ = base::RefCountedString::TakeString(&data);
+
+    IconImageRequest* image_request = new IconImageRequest(task_runner_, this);
+    ImageDecoder::Start(image_request, raw_icon_->data());
+  }
+
+  void ReportResultOnBlockingPool(LoadResult result) {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+
+    load_result_ = result;
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&IconLoader::ReportResultOnUIThread,
+                   base::Unretained(this)));
+  }
+
+  void NotifyClient() {
+    if (!client_)
+      return;
+
+    if (load_result_ == SUCCESS)
+      client_->OnIconLoadSuccess(icon_);
+    else
+      client_->OnIconLoadFailure();
+  }
+
+  void ReportResultOnUIThread() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+    NotifyClient();
+    delete this;
+  }
+
+  base::WeakPtr<KioskAppData> client_;
+  base::FilePath icon_path_;
+
+  LoadResult load_result_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  gfx::ImageSkia icon_;
+  scoped_refptr<base::RefCountedString> raw_icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconLoader);
+};
+
+////////////////////////////////////////////////////////////////////////////////
 // KioskAppData::WebstoreDataParser
 // Use WebstoreInstallHelper to parse the manifest and decode the icon.
 
@@ -266,14 +399,12 @@
                            const AccountId& account_id,
                            const GURL& update_url,
                            const base::FilePath& cached_crx)
-    : KioskAppDataBase(KioskAppManager::kKioskDictionaryName,
-                       app_id,
-                       account_id),
-      delegate_(delegate),
+    : delegate_(delegate),
       status_(STATUS_INIT),
+      app_id_(app_id),
+      account_id_(account_id),
       update_url_(update_url),
-      crx_file_(cached_crx),
-      weak_factory_(this) {}
+      crx_file_(cached_crx) {}
 
 KioskAppData::~KioskAppData() {}
 
@@ -286,6 +417,23 @@
   StartFetch();
 }
 
+void KioskAppData::ClearCache() {
+  PrefService* local_state = g_browser_process->local_state();
+
+  DictionaryPrefUpdate dict_update(local_state,
+                                   KioskAppManager::kKioskDictionaryName);
+
+  std::string app_key = std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
+  dict_update->Remove(app_key, NULL);
+
+  if (!icon_path_.empty()) {
+    base::PostTaskWithTraits(
+        FROM_HERE, base::TaskTraits().MayBlock().WithPriority(
+                       base::TaskPriority::BACKGROUND),
+        base::Bind(base::IgnoreResult(&base::DeleteFile), icon_path_, false));
+  }
+}
+
 void KioskAppData::LoadFromInstalledApp(Profile* profile,
                                         const extensions::Extension* app) {
   SetStatus(STATUS_LOADING);
@@ -293,10 +441,10 @@
   if (!app) {
     app = extensions::ExtensionSystem::Get(profile)
               ->extension_service()
-              ->GetInstalledExtension(app_id());
+              ->GetInstalledExtension(app_id_);
   }
 
-  DCHECK_EQ(app_id(), app->id());
+  DCHECK_EQ(app_id_, app->id());
 
   name_ = app->name();
   required_platform_version_ =
@@ -307,8 +455,7 @@
       app, kIconSize, ExtensionIconSet::MATCH_BIGGER);
   extensions::ImageLoader::Get(profile)->LoadImageAsync(
       app, image, gfx::Size(kIconSize, kIconSize),
-      base::Bind(&KioskAppData::OnExtensionIconLoaded,
-                 weak_factory_.GetWeakPtr()));
+      base::Bind(&KioskAppData::OnExtensionIconLoaded, AsWeakPtr()));
 }
 
 void KioskAppData::SetCachedCrx(const base::FilePath& crx_file) {
@@ -360,10 +507,10 @@
       break;
     case STATUS_LOADING:
     case STATUS_LOADED:
-      delegate_->OnKioskAppDataChanged(app_id());
+      delegate_->OnKioskAppDataChanged(app_id_);
       break;
     case STATUS_ERROR:
-      delegate_->OnKioskAppDataLoadFailure(app_id());
+      delegate_->OnKioskAppDataLoadFailure(app_id_);
       break;
   }
 }
@@ -373,51 +520,83 @@
 }
 
 bool KioskAppData::LoadFromCache() {
-  PrefService* local_state = g_browser_process->local_state();
-  const base::DictionaryValue* dict =
-      local_state->GetDictionary(dictionary_name());
-
-  if (!LoadFromDictionary(*dict))
-    return false;
-
-  const std::string app_key = std::string(kKeyApps) + '.' + app_id();
+  const std::string app_key =
+      std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
+  const std::string name_key = app_key + '.' + kKeyName;
+  const std::string icon_path_key = app_key + '.' + kKeyIcon;
   const std::string required_platform_version_key =
       app_key + '.' + kKeyRequiredPlatformVersion;
 
-  return dict->GetString(required_platform_version_key,
-                         &required_platform_version_);
+  PrefService* local_state = g_browser_process->local_state();
+  const base::DictionaryValue* dict =
+      local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
+
+  icon_path_.clear();
+  std::string icon_path_string;
+  if (!dict->GetString(name_key, &name_) ||
+      !dict->GetString(icon_path_key, &icon_path_string) ||
+      !dict->GetString(required_platform_version_key,
+                       &required_platform_version_)) {
+    return false;
+  }
+  icon_path_ = base::FilePath(icon_path_string);
+
+  // IconLoader deletes itself when done.
+  (new IconLoader(AsWeakPtr(), icon_path_))->Start();
+  return true;
+}
+
+void KioskAppData::SetCache(const std::string& name,
+                            const base::FilePath& icon_path,
+                            const std::string& required_platform_version) {
+  name_ = name;
+  icon_path_ = icon_path;
+  required_platform_version_ = required_platform_version;
+
+  const std::string app_key =
+      std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
+  const std::string name_key = app_key + '.' + kKeyName;
+  const std::string icon_path_key = app_key + '.' + kKeyIcon;
+  const std::string required_platform_version_key =
+      app_key + '.' + kKeyRequiredPlatformVersion;
+
+  PrefService* local_state = g_browser_process->local_state();
+  DictionaryPrefUpdate dict_update(local_state,
+                                   KioskAppManager::kKioskDictionaryName);
+  dict_update->SetString(name_key, name);
+  dict_update->SetString(icon_path_key, icon_path.value());
+  dict_update->SetString(required_platform_version_key,
+                         required_platform_version);
 }
 
 void KioskAppData::SetCache(const std::string& name,
                             const SkBitmap& icon,
                             const std::string& required_platform_version) {
-  name_ = name;
-  required_platform_version_ = required_platform_version;
   icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon);
   icon_.MakeThreadSafe();
 
+  std::vector<unsigned char> image_data;
+  CHECK(gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &image_data));
+  scoped_refptr<base::RefCountedString> raw_icon(new base::RefCountedString);
+  raw_icon->data().assign(image_data.begin(), image_data.end());
+
   base::FilePath cache_dir;
   if (delegate_)
     delegate_->GetKioskAppIconCacheDir(&cache_dir);
 
-  SaveIcon(icon, cache_dir);
+  base::FilePath icon_path =
+      cache_dir.AppendASCII(app_id_).AddExtension(kIconFileExtension);
+  BrowserThread::GetBlockingPool()->PostTask(
+      FROM_HERE,
+      base::Bind(&SaveIconToLocalOnBlockingPool, icon_path, raw_icon));
 
-  PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
-  SaveToDictionary(dict_update);
-
-  const std::string app_key = std::string(kKeyApps) + '.' + app_id();
-  const std::string required_platform_version_key =
-      app_key + '.' + kKeyRequiredPlatformVersion;
-
-  dict_update->SetString(required_platform_version_key,
-                         required_platform_version);
+  SetCache(name, icon_path, required_platform_version);
 }
 
 void KioskAppData::OnExtensionIconLoaded(const gfx::Image& icon) {
   if (icon.IsEmpty()) {
     LOG(WARNING) << "Failed to load icon from installed app"
-                 << ", id=" << app_id();
+                 << ", id=" << app_id_;
     SetCache(name_, *extensions::util::GetDefaultAppIcon().bitmap(),
              required_platform_version_);
   } else {
@@ -429,13 +608,11 @@
 
 void KioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  kiosk_app_icon_loader_.release();
   icon_ = icon;
   SetStatus(STATUS_LOADED);
 }
 
 void KioskAppData::OnIconLoadFailure() {
-  kiosk_app_icon_loader_.release();
   // Re-fetch data from web store when failed to load cached data.
   StartFetch();
 }
@@ -458,7 +635,10 @@
   }
 
   webstore_fetcher_.reset(new extensions::WebstoreDataFetcher(
-      this, GetRequestContextGetter(), GURL(), app_id()));
+      this,
+      GetRequestContextGetter(),
+      GURL(),
+      app_id_));
   webstore_fetcher_->set_max_auto_retries(3);
   webstore_fetcher_->Start();
 }
@@ -494,12 +674,15 @@
   }
 
   // WebstoreDataParser deletes itself when done.
-  (new WebstoreDataParser(weak_factory_.GetWeakPtr()))
-      ->Start(app_id(), manifest, icon_url, GetRequestContextGetter());
+  (new WebstoreDataParser(AsWeakPtr()))->Start(app_id_,
+                                               manifest,
+                                               icon_url,
+                                               GetRequestContextGetter());
 }
 
 void KioskAppData::OnWebstoreResponseParseFailure(const std::string& error) {
-  LOG(ERROR) << "Webstore failed for kiosk app " << app_id() << ", " << error;
+  LOG(ERROR) << "Webstore failed for kiosk app " << app_id_
+             << ", " << error;
   webstore_fetcher_.reset();
   SetStatus(STATUS_ERROR);
 }
@@ -520,8 +703,7 @@
   if (crx_file_.empty())
     return;
 
-  scoped_refptr<CrxLoader> crx_loader(
-      new CrxLoader(weak_factory_.GetWeakPtr(), crx_file_));
+  scoped_refptr<CrxLoader> crx_loader(new CrxLoader(AsWeakPtr(), crx_file_));
   crx_loader->Start();
 }
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.h b/chrome/browser/chromeos/app_mode/kiosk_app_data.h
index 2baa6a7..7f5d5c9 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.h
@@ -10,10 +10,11 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_app_data_base.h"
 #include "chrome/browser/extensions/webstore_data_fetcher_delegate.h"
 #include "components/signin/core/account_id/account_id.h"
+#include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -37,7 +38,7 @@
 
 // Fetches an app's web store data and manages the cached info such as name
 // and icon.
-class KioskAppData : public KioskAppDataBase,
+class KioskAppData : public base::SupportsWeakPtr<KioskAppData>,
                      public extensions::WebstoreDataFetcherDelegate {
  public:
   enum Status {
@@ -58,6 +59,9 @@
   // from web store.
   void Load();
 
+  // Clears locally cached data.
+  void ClearCache();
+
   // Loads app data from the app installed in the given profile.
   void LoadFromInstalledApp(Profile* profile, const extensions::Extension* app);
 
@@ -71,7 +75,11 @@
   // Returns true if the update url points to Webstore.
   bool IsFromWebStore() const;
 
+  const std::string& app_id() const { return app_id_; }
+  const AccountId& account_id() const { return account_id_; }
+  const std::string& name() const { return name_; }
   const GURL& update_url() const { return update_url_; }
+  const gfx::ImageSkia& icon() const { return icon_; }
   const std::string& required_platform_version() const {
     return required_platform_version_;
   }
@@ -86,12 +94,9 @@
       const GURL& update_url,
       const std::string& required_platform_version);
 
-  // Callbacks for KioskAppIconLoader.
-  void OnIconLoadSuccess(const gfx::ImageSkia& icon) override;
-  void OnIconLoadFailure() override;
-
  private:
   class CrxLoader;
+  class IconLoader;
   class WebstoreDataParser;
 
   void SetStatus(Status status);
@@ -104,12 +109,21 @@
 
   // Sets the cached data.
   void SetCache(const std::string& name,
+                const base::FilePath& icon_path,
+                const std::string& required_platform_version);
+
+  // Helper to set the cached data using a SkBitmap icon.
+  void SetCache(const std::string& name,
                 const SkBitmap& icon,
                 const std::string& required_platform_version);
 
   // Callback for extensions::ImageLoader.
   void OnExtensionIconLoaded(const gfx::Image& icon);
 
+  // Callbacks for IconLoader.
+  void OnIconLoadSuccess(const gfx::ImageSkia& icon);
+  void OnIconLoadFailure();
+
   // Callbacks for WebstoreDataParser
   void OnWebstoreParseSuccess(const SkBitmap& icon,
                               const std::string& required_platform_version);
@@ -140,15 +154,18 @@
   KioskAppDataDelegate* delegate_;  // not owned.
   Status status_;
 
+  std::string app_id_;
+  AccountId account_id_;
+  std::string name_;
   GURL update_url_;
+  gfx::ImageSkia icon_;
   std::string required_platform_version_;
 
   std::unique_ptr<extensions::WebstoreDataFetcher> webstore_fetcher_;
+  base::FilePath icon_path_;
 
   base::FilePath crx_file_;
 
-  base::WeakPtrFactory<KioskAppData> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(KioskAppData);
 };
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data_base.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data_base.cc
deleted file mode 100644
index 759a887..0000000
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data_base.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/app_mode/kiosk_app_data_base.h"
-
-#include <utility>
-
-#include "base/files/file_util.h"
-#include "base/memory/ptr_util.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/browser_process.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/gfx/codec/png_codec.h"
-
-using content::BrowserThread;
-
-namespace chromeos {
-
-namespace {
-
-// Keys for local state data.
-constexpr char kKeyName[] = "name";
-constexpr char kKeyIcon[] = "icon";
-
-// Icon file extension.
-constexpr char kIconFileExtension[] = ".png";
-
-// Save |raw_icon| for given |app_id|.
-void SaveIconToLocalOnBlockingPool(const base::FilePath& icon_path,
-                                   std::vector<unsigned char> image_data) {
-  DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
-
-  const base::FilePath dir = icon_path.DirName();
-  if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
-    LOG(ERROR) << "Failed to create directory to store kiosk icons";
-    return;
-  }
-
-  const int wrote = base::WriteFile(
-      icon_path, reinterpret_cast<char*>(image_data.data()), image_data.size());
-  if (wrote != static_cast<int>(image_data.size())) {
-    LOG(ERROR) << "Failed to write kiosk icon file";
-  }
-}
-
-}  // namespace
-
-// static
-const char KioskAppDataBase::kKeyApps[] = "apps";
-
-KioskAppDataBase::KioskAppDataBase(const std::string& dictionary_name,
-                                   const std::string& app_id,
-                                   const AccountId& account_id)
-    : dictionary_name_(dictionary_name),
-      app_id_(app_id),
-      account_id_(account_id) {}
-
-KioskAppDataBase::~KioskAppDataBase() = default;
-
-void KioskAppDataBase::SaveToDictionary(DictionaryPrefUpdate& dict_update) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const std::string app_key = std::string(kKeyApps) + '.' + app_id_;
-  const std::string name_key = app_key + '.' + kKeyName;
-  const std::string icon_path_key = app_key + '.' + kKeyIcon;
-
-  dict_update->SetString(name_key, name_);
-  dict_update->SetString(icon_path_key, icon_path_.value());
-}
-
-bool KioskAppDataBase::LoadFromDictionary(const base::DictionaryValue& dict) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const std::string app_key =
-      std::string(KioskAppDataBase::kKeyApps) + '.' + app_id_;
-  const std::string name_key = app_key + '.' + kKeyName;
-  const std::string icon_path_key = app_key + '.' + kKeyIcon;
-
-  std::string icon_path_string;
-  if (!dict.GetString(name_key, &name_) ||
-      !dict.GetString(icon_path_key, &icon_path_string)) {
-    return false;
-  }
-  icon_path_ = base::FilePath(icon_path_string);
-
-  kiosk_app_icon_loader_ = base::MakeUnique<KioskAppIconLoader>(this);
-  kiosk_app_icon_loader_->Start(icon_path_);
-  return true;
-}
-
-void KioskAppDataBase::SaveIcon(const SkBitmap& icon,
-                                const base::FilePath& cache_dir) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::vector<unsigned char> image_data;
-  if (!gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &image_data)) {
-    LOG(ERROR) << "Failed to encode kiosk icon";
-    return;
-  }
-
-  const base::FilePath icon_path =
-      cache_dir.AppendASCII(app_id_).AddExtension(kIconFileExtension);
-  BrowserThread::GetBlockingPool()->PostTask(
-      FROM_HERE, base::Bind(&SaveIconToLocalOnBlockingPool, icon_path,
-                            base::Passed(std::move(image_data))));
-
-  icon_path_ = icon_path;
-}
-
-void KioskAppDataBase::ClearCache() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  PrefService* local_state = g_browser_process->local_state();
-
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
-
-  const std::string app_key =
-      std::string(KioskAppDataBase::kKeyApps) + '.' + app_id_;
-  dict_update->Remove(app_key, nullptr);
-
-  if (!icon_path_.empty()) {
-    base::PostTaskWithTraits(
-        FROM_HERE,
-        base::TaskTraits().MayBlock().WithPriority(
-            base::TaskPriority::BACKGROUND),
-        base::Bind(base::IgnoreResult(&base::DeleteFile), icon_path_, false));
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data_base.h b/chrome/browser/chromeos/app_mode/kiosk_app_data_base.h
deleted file mode 100644
index 626c599..0000000
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data_base.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_DATA_BASE_H_
-#define CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_DATA_BASE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace base {
-class DictionaryValue;
-}
-
-namespace chromeos {
-
-class KioskAppDataBase : public KioskAppIconLoader::Delegate {
- public:
-  // Dictionary key for apps.
-  static const char kKeyApps[];
-
-  const std::string& dictionary_name() const { return dictionary_name_; }
-  const std::string& app_id() const { return app_id_; }
-  const AccountId& account_id() const { return account_id_; }
-  const std::string& name() const { return name_; }
-  const gfx::ImageSkia& icon() const { return icon_; }
-
-  // Callbacks for KioskAppIconLoader.
-  void OnIconLoadSuccess(const gfx::ImageSkia& icon) override = 0;
-  void OnIconLoadFailure() override = 0;
-
-  // Clears locally cached data.
-  void ClearCache();
-
- protected:
-  KioskAppDataBase(const std::string& dictionary_name,
-                   const std::string& app_id,
-                   const AccountId& account_id);
-  ~KioskAppDataBase() override;
-
-  // Helper to save name and icon to provided dictionary.
-  void SaveToDictionary(DictionaryPrefUpdate& dict_update);
-
-  // Helper to load name and icon from provided dictionary.
-  bool LoadFromDictionary(const base::DictionaryValue& dict);
-
-  // Helper to cache |icon| to |cache_dir|.
-  void SaveIcon(const SkBitmap& icon, const base::FilePath& cache_dir);
-
-  // In protected section to allow derived classes to modify.
-  std::string name_;
-  gfx::ImageSkia icon_;
-
-  // Should be released when callbacks are called.
-  std::unique_ptr<KioskAppIconLoader> kiosk_app_icon_loader_;
-
- private:
-  // Name of a dictionary that holds kiosk app info in Local State.
-  const std::string dictionary_name_;
-
-  const std::string app_id_;
-  const AccountId account_id_;
-
-  base::FilePath icon_path_;
-
-  DISALLOW_COPY_AND_ASSIGN(KioskAppDataBase);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_DATA_BASE_H_
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.cc b/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.cc
deleted file mode 100644
index cb46f50a..0000000
--- a/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/image_decoder.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
-
-namespace chromeos {
-
-class IconImageRequest : public ImageDecoder::ImageRequest {
- public:
-  IconImageRequest(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-                   KioskAppIconLoader::ResultCallback result_callback)
-      : ImageRequest(task_runner), result_callback_(result_callback) {}
-
-  void OnImageDecoded(const SkBitmap& decoded_image) override {
-    gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
-    image.MakeThreadSafe();
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::Bind(result_callback_, image));
-    delete this;
-  }
-
-  void OnDecodeImageFailed() override {
-    LOG(ERROR) << "Failed to decode icon image.";
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(result_callback_, base::Optional<gfx::ImageSkia>()));
-    delete this;
-  }
-
- private:
-  ~IconImageRequest() override = default;
-  KioskAppIconLoader::ResultCallback result_callback_;
-};
-
-void LoadOnBlockingPool(
-    const base::FilePath& icon_path,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    KioskAppIconLoader::ResultCallback result_callback) {
-  DCHECK(callback_task_runner->RunsTasksOnCurrentThread());
-
-  std::string data;
-  if (!base::ReadFileToString(base::FilePath(icon_path), &data)) {
-    LOG(ERROR) << "Failed to read icon file.";
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(result_callback, base::Optional<gfx::ImageSkia>()));
-    return;
-  }
-
-  // IconImageRequest will delete itself on completion of ImageDecoder callback.
-  IconImageRequest* image_request =
-      new IconImageRequest(callback_task_runner, result_callback);
-  ImageDecoder::Start(image_request,
-                      std::vector<uint8_t>(data.begin(), data.end()));
-}
-
-KioskAppIconLoader::KioskAppIconLoader(Delegate* delegate)
-    : delegate_(delegate), weak_factory_(this) {}
-
-KioskAppIconLoader::~KioskAppIconLoader() = default;
-
-void KioskAppIconLoader::Start(const base::FilePath& icon_path) {
-  base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
-  base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
-  task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
-      token, base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&LoadOnBlockingPool, icon_path, task_runner_,
-                 base::Bind(&KioskAppIconLoader::OnImageDecodingFinished,
-                            weak_factory_.GetWeakPtr())));
-}
-
-void KioskAppIconLoader::OnImageDecodingFinished(
-    base::Optional<gfx::ImageSkia> result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  if (result.has_value()) {
-    delegate_->OnIconLoadSuccess(result.value());
-  } else {
-    delegate_->OnIconLoadFailure();
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.h b/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.h
deleted file mode 100644
index 345ee9a..0000000
--- a/chrome/browser/chromeos/app_mode/kiosk_app_icon_loader.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_ICON_LOADER_H_
-#define CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_ICON_LOADER_H_
-
-#include "base/callback_forward.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/sequenced_task_runner.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace base {
-class FilePath;
-}
-
-namespace chromeos {
-
-// Loads locally stored icon data and decodes it.
-class KioskAppIconLoader {
- public:
-  enum LoadResult {
-    SUCCESS,
-    FAILED_TO_LOAD,
-    FAILED_TO_DECODE,
-  };
-
-  class Delegate {
-   public:
-    virtual void OnIconLoadSuccess(const gfx::ImageSkia& icon) = 0;
-    virtual void OnIconLoadFailure() = 0;
-
-   protected:
-    virtual ~Delegate() = default;
-  };
-
-  using ResultCallback =
-      base::Callback<void(base::Optional<gfx::ImageSkia> result)>;
-
-  explicit KioskAppIconLoader(Delegate* delegate);
-
-  ~KioskAppIconLoader();
-
-  void Start(const base::FilePath& icon_path);
-
- private:
-  void OnImageDecodingFinished(base::Optional<gfx::ImageSkia> result);
-
-  // Delegate always lives longer than this class as it's owned by delegate.
-  Delegate* const delegate_;
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  gfx::ImageSkia icon_;
-
-  base::WeakPtrFactory<KioskAppIconLoader> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(KioskAppIconLoader);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_ICON_LOADER_H_
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index 3554db54..a4bba2a 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -163,6 +163,7 @@
 
 // static
 const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
+const char KioskAppManager::kKeyApps[] = "apps";
 const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
 const char KioskAppManager::kIconCacheDir[] = "kiosk/icon";
 const char KioskAppManager::kCrxCacheDir[] = "kiosk/crx";
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
index d15455f9..7d7bbdf 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
@@ -87,6 +87,7 @@
   //     "auto_login_enabled": true  //
   //   }
   static const char kKioskDictionaryName[];
+  static const char kKeyApps[];
   static const char kKeyAutoLoginState[];
 
   // Sub directory under DIR_USER_DATA to store cached icon files.
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
index 8cb8d09..dafab9a 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
@@ -325,7 +325,7 @@
     PrefService* local_state = g_browser_process->local_state();
     DictionaryPrefUpdate dict_update(local_state,
                                      KioskAppManager::kKioskDictionaryName);
-    dict_update->Set(KioskAppDataBase::kKeyApps, std::move(apps_dict));
+    dict_update->Set(KioskAppManager::kKeyApps, std::move(apps_dict));
 
     // Make the app appear in device settings.
     base::ListValue device_local_accounts;
@@ -542,7 +542,7 @@
   const base::DictionaryValue* dict =
       local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
   const base::DictionaryValue* apps_dict;
-  EXPECT_TRUE(dict->GetDictionary(KioskAppDataBase::kKeyApps, &apps_dict));
+  EXPECT_TRUE(dict->GetDictionary(KioskAppManager::kKeyApps, &apps_dict));
   EXPECT_TRUE(apps_dict->HasKey("app_1"));
 
   manager()->ClearAppData("app_1");
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
index 0aea7cb..f628eb5 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
@@ -147,7 +147,7 @@
       base::MakeUnique<base::ListValue>());
   for (const auto& entry : certificates) {
     const base::DictionaryValue* certificate = nullptr;
-    if (!entry->GetAsDictionary(&certificate)) {
+    if (!entry.GetAsDictionary(&certificate)) {
       DLOG(FATAL) << "Value of a certificate entry is not a dictionary "
                   << "value.";
       continue;
@@ -168,7 +168,7 @@
     bool web_trust_flag = false;
     for (const auto& list_val : *trust_list) {
       std::string trust_type;
-      if (!list_val->GetAsString(&trust_type))
+      if (!list_val.GetAsString(&trust_type))
         NOTREACHED();
 
       if (trust_type == ::onc::certificate::kWeb) {
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 0938b0d..b32a295 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -763,7 +763,7 @@
       for (auto it2 = list_value->begin(); it2 != list_value->end(); ++it2) {
         // Try to read as dictionary.
         const base::DictionaryValue *dict_value;
-        if ((*it2)->GetAsDictionary(&dict_value)) {
+        if (it2->GetAsDictionary(&dict_value)) {
           if (dict_value->size() != 1) {
             LOG(ERROR) << extension->id()
                        << " has dict in permission list with size "
@@ -785,7 +785,7 @@
         }
         // Try to read as string.
         std::string permission_string;
-        if (!(*it2)->GetAsString(&permission_string)) {
+        if (!it2->GetAsString(&permission_string)) {
           LOG(ERROR) << extension->id() << ": " << it.key()
                      << " contains a token that's neither a string nor a dict.";
           safe = false;
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
index 46a5bec..d38cf28 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
@@ -108,7 +108,7 @@
   GetSanitizedPolicyPinMinMaxLength(pref_service, &min_length, &max_length);
 
   // Check if the PIN is shorter than the minimum specified length.
-  if (int{pin.size()} < min_length) {
+  if (static_cast<int>(pin.size()) < min_length) {
     if (length_problem)
       *length_problem = CredentialProblem::CREDENTIAL_PROBLEM_TOO_SHORT;
     return false;
@@ -116,7 +116,7 @@
 
   // If the maximum specified length is zero, there is no maximum length.
   // Otherwise check if the PIN is longer than the maximum specified length.
-  if (max_length != 0 && int{pin.size()} > max_length) {
+  if (max_length != 0 && static_cast<int>(pin.size()) > max_length) {
     if (length_problem)
       *length_problem = CredentialProblem::CREDENTIAL_PROBLEM_TOO_LONG;
     return false;
@@ -135,7 +135,7 @@
 bool IsPinDifficultEnough(const std::string& pin) {
   // If the pin length is |kMinLengthForNonWeakPin| or less, there is no need to
   // check for same character and increasing pin.
-  if (int{pin.size()} <= kMinLengthForNonWeakPin)
+  if (static_cast<int>(pin.size()) <= kMinLengthForNonWeakPin)
     return true;
 
   // Check if it is on the list of most common PINs.
@@ -149,7 +149,7 @@
   // TODO(sammiequon): Should longer PINs (5+) be still subjected to this?
   bool is_increasing = true;
   bool is_decreasing = true;
-  for (int i = 1; i < int{pin.length()}; ++i) {
+  for (int i = 1; i < static_cast<int>(pin.length()); ++i) {
     const char previous = pin[i - 1];
     const char current = pin[i];
 
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
index 20013f9..a249299 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -114,10 +114,9 @@
 
     base::ListValue* list = nullptr;
     EXPECT_TRUE(result->GetAsList(&list));
-    // Consume the unique_ptr by reference so we don't take ownership.
-    for (const std::unique_ptr<base::Value>& value : (*list)) {
+    for (const base::Value& value : *list) {
       std::string mode;
-      EXPECT_TRUE(value->GetAsString(&mode));
+      EXPECT_TRUE(value.GetAsString(&mode));
       modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode));
     }
 
@@ -134,9 +133,9 @@
 
     base::ListValue* list = nullptr;
     EXPECT_TRUE(result->GetAsList(&list));
-    for (const std::unique_ptr<base::Value>& value : *list) {
+    for (const base::Value& value : *list) {
       std::string mode;
-      EXPECT_TRUE(value->GetAsString(&mode));
+      EXPECT_TRUE(value.GetAsString(&mode));
       modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode));
     }
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index ebab6deb..e1769d3f 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -17,10 +17,13 @@
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_manager/mount_test_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/common/chrome_switches.h"
@@ -30,6 +33,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/test/test_api.h"
+#include "extensions/browser/extension_system.h"
 #include "extensions/browser/notification_types.h"
 #include "google_apis/drive/drive_api_parser.h"
 #include "google_apis/drive/test_util.h"
@@ -525,6 +529,18 @@
   ExtensionApiTest::SetUpOnMainThread();
   ASSERT_TRUE(local_volume_->Mount(profile()));
 
+  // The file manager component app should have been added for loading into the
+  // user profile, but not into the sign-in profile.
+  ASSERT_TRUE(extensions::ExtensionSystem::Get(profile())
+                  ->extension_service()
+                  ->component_loader()
+                  ->Exists(kFileManagerAppId));
+  ASSERT_FALSE(extensions::ExtensionSystem::Get(
+                   chromeos::ProfileHelper::GetSigninProfile())
+                   ->extension_service()
+                   ->component_loader()
+                   ->Exists(kFileManagerAppId));
+
   if (GetGuestModeParam() != IN_GUEST_MODE) {
     // Install the web server to serve the mocked share dialog.
     ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/chromeos/file_system_provider/registry.cc b/chrome/browser/chromeos/file_system_provider/registry.cc
index bc0e4c3ba..e9b0ef9 100644
--- a/chrome/browser/chromeos/file_system_provider/registry.cc
+++ b/chrome/browser/chromeos/file_system_provider/registry.cc
@@ -228,7 +228,7 @@
         restored_watcher.last_tag = last_tag;
         for (const auto& persistent_origin : *persistent_origins) {
           std::string origin;
-          if (persistent_origin->GetAsString(&origin)) {
+          if (persistent_origin.GetAsString(&origin)) {
             LOG(ERROR) << "Malformed subscriber information in preferences.";
             continue;
           }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc
index f2f7d36..94a5e89 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc
@@ -184,7 +184,7 @@
        it != device_list.end();
        ++it) {
     const base::DictionaryValue* dict;
-    if (!(*it)->GetAsDictionary(&dict) || !dict)
+    if (!it->GetAsDictionary(&dict) || !dict)
       return false;
 
     EasyUnlockDeviceKeyData data;
diff --git a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
index affc58b..f912b35 100644
--- a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
@@ -132,7 +132,7 @@
     for (base::ListValue::const_iterator it = list->begin(); it != list->end();
          ++it) {
       std::string locale;
-      if (!(*it)->GetAsString(&locale)) {
+      if (!it->GetAsString(&locale)) {
         NOTREACHED();
         new_recommended_locales.clear();
         break;
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
index 39a9d04..4259daa 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -474,7 +474,7 @@
            entry != accounts_list->end();
            ++entry) {
         const base::DictionaryValue* entry_dict = NULL;
-        if ((*entry)->GetAsDictionary(&entry_dict)) {
+        if (entry->GetAsDictionary(&entry_dict)) {
           em::DeviceLocalAccountInfoProto* account =
               device_local_accounts->add_account();
           std::string account_id;
@@ -572,7 +572,7 @@
            i != users->end();
            ++i) {
         std::string email;
-        if ((*i)->GetAsString(&email))
+        if (i->GetAsString(&email))
           whitelist_proto->add_user_whitelist(email);
       }
     }
@@ -604,7 +604,7 @@
            i != flags->end();
            ++i) {
         std::string flag;
-        if ((*i)->GetAsString(&flag))
+        if (i->GetAsString(&flag))
           flags_proto->add_flags(flag);
       }
     }
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions.cc b/chrome/browser/chromeos/platform_keys/key_permissions.cc
index e02671d6..4957a74d 100644
--- a/chrome/browser/chromeos/platform_keys/key_permissions.cc
+++ b/chrome/browser/chromeos/platform_keys/key_permissions.cc
@@ -247,20 +247,15 @@
     return;
   }
   for (const auto& entry : *entries) {
-    if (!entry) {
-      LOG(ERROR) << "Found invalid NULL entry in PlatformKeys state store.";
-      continue;
-    }
-
     std::string spki_b64;
     const base::DictionaryValue* dict_entry = nullptr;
-    if (entry->GetAsString(&spki_b64)) {
+    if (entry.GetAsString(&spki_b64)) {
       // This handles the case that the store contained a plain list of base64
       // and DER-encoded SPKIs from an older version of ChromeOS.
       KeyEntry new_entry(spki_b64);
       new_entry.sign_once = true;
       state_store_entries_.push_back(new_entry);
-    } else if (entry->GetAsDictionary(&dict_entry)) {
+    } else if (entry.GetAsDictionary(&dict_entry)) {
       dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI, &spki_b64);
       KeyEntry new_entry(spki_b64);
       dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce,
@@ -269,7 +264,7 @@
                                                  &new_entry.sign_unlimited);
       state_store_entries_.push_back(new_entry);
     } else {
-      LOG(ERROR) << "Found invalid entry of type " << entry->GetType()
+      LOG(ERROR) << "Found invalid entry of type " << entry.GetType()
                  << " in PlatformKeys state store.";
       continue;
     }
diff --git a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
index 082458844..aa75b6e2 100644
--- a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
@@ -324,7 +324,7 @@
     for (base::ListValue::const_iterator entry(policy_list->begin());
          entry != policy_list->end(); ++entry) {
       std::string id;
-      if ((*entry)->GetAsString(&id)) {
+      if (entry->GetAsString(&id)) {
         auto app_dict = base::MakeUnique<base::DictionaryValue>();
         app_dict->SetString(ash::launcher::kPinnedAppsPrefAppIDPath, id);
         pinned_apps_list->Append(std::move(app_dict));
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
index e6123b8..fef9650 100644
--- a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
+++ b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
@@ -186,7 +186,7 @@
 
 void AppendAll(const base::ListValue& from, base::ListValue* to) {
   for (const auto& value : from)
-    to->Append(value->CreateDeepCopy());
+    to->Append(value.CreateDeepCopy());
 }
 
 // Matcher to match base::Value.
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 8ddab2a..14ec86b2 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -195,8 +195,8 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterIntegerPref(
       prefs::kAccessibilityAutoclickDelayMs,
-      int{ash::AutoclickController::GetDefaultAutoclickDelay()
-              .InMilliseconds()},
+      static_cast<int>(ash::AutoclickController::GetDefaultAutoclickDelay()
+                           .InMilliseconds()),
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(
       prefs::kAccessibilityVirtualKeyboardEnabled,
diff --git a/chrome/browser/chromeos/printing/printers_manager.cc b/chrome/browser/chromeos/printing/printers_manager.cc
index 0cded68..4bcef70 100644
--- a/chrome/browser/chromeos/printing/printers_manager.cc
+++ b/chrome/browser/chromeos/printing/printers_manager.cc
@@ -180,7 +180,7 @@
   recommended_printer_ids_.clear();
   for (const auto& value : *values) {
     std::string printer_json;
-    if (!value->GetAsString(&printer_json)) {
+    if (!value.GetAsString(&printer_json)) {
       NOTREACHED();
       continue;
     }
diff --git a/chrome/browser/chromeos/settings/cros_settings.cc b/chrome/browser/chromeos/settings/cros_settings.cc
index 91042aac4..b49a02e4 100644
--- a/chrome/browser/chromeos/settings/cros_settings.cc
+++ b/chrome/browser/chromeos/settings/cros_settings.cc
@@ -245,7 +245,7 @@
        entry != list->end();
        ++entry) {
     std::string entry_string;
-    if (!(*entry)->GetAsString(&entry_string)) {
+    if (!entry->GetAsString(&entry_string)) {
       NOTREACHED();
       continue;
     }
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc
index d8019d4..bca424f8 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc
@@ -209,7 +209,7 @@
   safe_browsing::SwReporterQueue invocations;
   for (const auto& iter : *parameter_list) {
     const base::DictionaryValue* invocation_params = nullptr;
-    if (!iter->GetAsDictionary(&invocation_params)) {
+    if (!iter.GetAsDictionary(&invocation_params)) {
       ReportExperimentError(SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS);
       return;
     }
@@ -240,7 +240,7 @@
     std::vector<base::string16> argv = {exe_path.value()};
     for (const auto& value : *arguments) {
       base::string16 argument;
-      if (!value->GetAsString(&argument)) {
+      if (!value.GetAsString(&argument)) {
         ReportExperimentError(SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS);
         return;
       }
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index d2008b54..3939cc6e 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -262,12 +262,12 @@
     return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
                                                          kLocationsParam);
   for (const auto& item : *locations) {
-    if (!item->IsType(base::Value::Type::DICTIONARY)) {
+    if (!item.IsType(base::Value::Type::DICTIONARY)) {
       return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
                                                            kLocationsParam);
     }
-    base::DictionaryValue* dictionary =
-        static_cast<base::DictionaryValue*>(item.get());
+    const base::DictionaryValue* dictionary =
+        static_cast<const base::DictionaryValue*>(&item);
     std::string host;
     if (!dictionary->GetStringWithoutPathExpansion(kHostParam, &host)) {
       return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
diff --git a/chrome/browser/devtools/device/devtools_device_discovery.cc b/chrome/browser/devtools/device/devtools_device_discovery.cc
index 7c2f83a..306b2506 100644
--- a/chrome/browser/devtools/device/devtools_device_discovery.cc
+++ b/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -483,8 +483,8 @@
   base::ListValue* list_value;
   if (value && value->GetAsList(&list_value)) {
     for (const auto& page_value : *list_value) {
-      base::DictionaryValue* dict;
-      if (page_value->GetAsDictionary(&dict))
+      const base::DictionaryValue* dict;
+      if (page_value.GetAsDictionary(&dict))
         browser->pages_.push_back(
             new RemotePage(device, browser->browser_id_, *dict));
     }
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 397292a..80cb32e3 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -67,8 +67,7 @@
 struct ParamTuple<T, Ts...> {
   bool Parse(const base::ListValue& list,
              const base::ListValue::const_iterator& it) {
-    return it != list.end() && GetValue(**it, &head) &&
-           tail.Parse(list, it + 1);
+    return it != list.end() && GetValue(*it, &head) && tail.Parse(list, it + 1);
   }
 
   template <typename H, typename... As>
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 6993861..14aefab9 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -257,7 +257,7 @@
   base::ListValue::iterator it = shortcut_list->begin();
   for (; it != shortcut_list->end(); ++it) {
     base::DictionaryValue* dictionary;
-    if (!(*it)->GetAsDictionary(&dictionary))
+    if (!it->GetAsDictionary(&dictionary))
       continue;
     int key_code = 0;
     dictionary->GetInteger("keyCode", &key_code);
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index d5f20a80..65176e3 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -60,29 +60,6 @@
   ~MockWebContentsDelegate() override {}
 };
 
-// Google Mock action that posts a task to the current message loop that invokes
-// the first argument of the mocked method as a callback. Said argument must be
-// a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is
-// bound as that parameter.
-// Example:
-//   class FooClass {
-//    public:
-//     virtual void Foo(base::Callback<void(bool)> callback);
-//   };
-//   ...
-//   EXPECT_CALL(mock_fooclass_instance, Foo(callback))
-//     .WillOnce(ScheduleCallback(false));
-ACTION_P(ScheduleCallback, result) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(arg0, result));
-}
-
-// Similar to ScheduleCallback, but binds 2 arguments.
-ACTION_P2(ScheduleCallback2, result0, result1) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(arg0, result0, result1));
-}
-
 // Subclass of the ChromeDownloadManagerDelegate that uses a mock
 // DownloadProtectionService.
 class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 1dc2967..3697602 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -298,6 +298,12 @@
     "api/networking_private/networking_private_ui_delegate_chromeos.h",
     "api/networking_private/networking_private_ui_delegate_factory_impl.cc",
     "api/networking_private/networking_private_ui_delegate_factory_impl.h",
+    "api/notifications/extension_notification_display_helper.cc",
+    "api/notifications/extension_notification_display_helper.h",
+    "api/notifications/extension_notification_display_helper_factory.cc",
+    "api/notifications/extension_notification_display_helper_factory.h",
+    "api/notifications/extension_notification_handler.cc",
+    "api/notifications/extension_notification_handler.h",
     "api/notifications/notifications_api.cc",
     "api/notifications/notifications_api.h",
     "api/omnibox/omnibox_api.cc",
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_store.cc b/chrome/browser/extensions/api/content_settings/content_settings_store.cc
index 8bed231..6f2a579 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_store.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_store.cc
@@ -294,8 +294,8 @@
     const base::ListValue* list,
     ExtensionPrefsScope scope) {
   for (const auto& value : *list) {
-    base::DictionaryValue* dict;
-    if (!value->GetAsDictionary(&dict)) {
+    const base::DictionaryValue* dict = nullptr;
+    if (!value.GetAsDictionary(&dict)) {
       NOTREACHED();
       continue;
     }
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index 6da57a1..5185a41 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -170,7 +170,7 @@
        it != append_strings.end();
        ++it) {
     std::string value;
-    if ((*it)->GetAsString(&value)) {
+    if (it->GetAsString(&value)) {
       append_to->push_back(value);
     } else {
       return false;
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
index 396d22bc..e5ecb2c 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
@@ -55,7 +55,7 @@
        it != parsed_list->end();
        ++it) {
     const base::DictionaryValue* dict;
-    CHECK((*it)->GetAsDictionary(&dict));
+    CHECK(it->GetAsDictionary(&dict));
     actions.push_back(dict->CreateDeepCopy());
   }
 
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index b3e2457e..31f9f9d 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -301,7 +301,7 @@
   for (base::ListValue::iterator it = fonts->begin();
        it != fonts->end(); ++it) {
     base::ListValue* font_list_value;
-    if (!(*it)->GetAsList(&font_list_value)) {
+    if (!it->GetAsList(&font_list_value)) {
       NOTREACHED();
       return false;
     }
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index d21fdb8..db214edb 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -422,7 +422,7 @@
          it != results->end();
          ++it) {
       std::unique_ptr<api::identity::AccountInfo> info =
-          api::identity::AccountInfo::FromValue(**it);
+          api::identity::AccountInfo::FromValue(*it);
       if (info.get())
         result_ids.insert(info->id);
       else
@@ -454,11 +454,11 @@
     } else {
       for (const auto& result : *results) {
         std::unique_ptr<api::identity::AccountInfo> info =
-            api::identity::AccountInfo::FromValue(*result);
+            api::identity::AccountInfo::FromValue(result);
         if (info.get())
           msg << info->id << " ";
         else
-          msg << *result << "<-" << result->GetType() << " ";
+          msg << result << "<-" << result.GetType() << " ";
       }
     }
 
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
index e54984f..b5106121 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
@@ -112,7 +112,7 @@
   for (base::ListValue::const_iterator it = allowed_origins_list->begin();
        it != allowed_origins_list->end(); ++it) {
     std::string pattern_string;
-    if (!(*it)->GetAsString(&pattern_string)) {
+    if (!it->GetAsString(&pattern_string)) {
       *error_message = "allowed_origins must be list of strings.";
       return false;
     }
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc b/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
index bb153dc8..8f9410f0 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
@@ -68,7 +68,7 @@
   for (base::ListValue::const_iterator entry(list_value->begin());
        entry != list_value->end(); ++entry) {
     std::string name;
-    if (!(*entry)->GetAsString(&name)) {
+    if (!entry->GetAsString(&name)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::STRING));
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc b/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc
new file mode 100644
index 0000000..cfbdfc4
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc
@@ -0,0 +1,95 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
+
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+ExtensionNotificationDisplayHelper::ExtensionNotificationDisplayHelper(
+    Profile* profile)
+    : profile_(profile) {}
+
+ExtensionNotificationDisplayHelper::~ExtensionNotificationDisplayHelper() {}
+
+void ExtensionNotificationDisplayHelper::Display(
+    const Notification& notification) {
+  // Remove the previous version of this notification if the |notification| is
+  // updating another notification.
+  EraseDataForNotificationId(notification.delegate_id());
+
+  notifications_.push_back(base::MakeUnique<Notification>(notification));
+
+  GetDisplayService()->Display(NotificationCommon::EXTENSION,
+                               notification.delegate_id(), notification);
+}
+
+Notification* ExtensionNotificationDisplayHelper::GetByNotificationId(
+    const std::string& notification_id) {
+  for (const auto& notification : notifications_) {
+    if (notification->delegate_id() == notification_id)
+      return notification.get();
+  }
+
+  return nullptr;
+}
+
+std::set<std::string>
+ExtensionNotificationDisplayHelper::GetNotificationIdsForExtension(
+    const GURL& extension_origin) const {
+  std::set<std::string> notification_ids;
+  for (const auto& notification : notifications_) {
+    if (notification->origin_url() == extension_origin)
+      notification_ids.insert(notification->delegate_id());
+  }
+
+  return notification_ids;
+}
+
+bool ExtensionNotificationDisplayHelper::EraseDataForNotificationId(
+    const std::string& notification_id) {
+  auto iter = std::find_if(
+      notifications_.begin(), notifications_.end(),
+      [notification_id](const std::unique_ptr<Notification>& notification) {
+        return notification->delegate_id() == notification_id;
+      });
+
+  if (iter == notifications_.end())
+    return false;
+
+  notifications_.erase(iter);
+  return true;
+}
+
+bool ExtensionNotificationDisplayHelper::Close(
+    const std::string& notification_id) {
+  if (!EraseDataForNotificationId(notification_id))
+    return false;
+
+  GetDisplayService()->Close(NotificationCommon::EXTENSION, notification_id);
+  return true;
+}
+
+void ExtensionNotificationDisplayHelper::Shutdown() {
+  // Do not call GetDisplayService()->Close() here. The Shutdown method is
+  // called upon profile destruction and closing a notification requires a
+  // profile, as it may dispatch an event to the extension.
+
+  notifications_.clear();
+}
+
+NotificationDisplayService*
+ExtensionNotificationDisplayHelper::GetDisplayService() {
+  return NotificationDisplayServiceFactory::GetForProfile(profile_);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_display_helper.h b/chrome/browser/extensions/api/notifications/extension_notification_display_helper.h
new file mode 100644
index 0000000..1ebf8fb
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_display_helper.h
@@ -0,0 +1,72 @@
+// 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_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class GURL;
+class Notification;
+class NotificationDisplayService;
+class Profile;
+
+namespace extensions {
+
+// Helper class for displaying notifications through the notification display
+// service. The NDS supports notifications that can outlive the browser process,
+// and therefore does not retain as much information as is necessary to support
+// the extensions API. (Notably the ability to partly update notifications.)
+class ExtensionNotificationDisplayHelper : public KeyedService {
+ public:
+  explicit ExtensionNotificationDisplayHelper(Profile* profile);
+  ~ExtensionNotificationDisplayHelper() override;
+
+  // Displays the |notification| using the notification display service.
+  void Display(const Notification& notification);
+
+  // Returns the notification identified by |notification_id| if it is currently
+  // visible. May return a nullptr.
+  Notification* GetByNotificationId(const std::string& notification_id);
+
+  // Returns a set with the IDs of all notifications that are currently being
+  // shown on behalf of the |extension_origin|.
+  std::set<std::string> GetNotificationIdsForExtension(
+      const GURL& extension_origin) const;
+
+  // Removes stored state for the notification identified by |notification_id|.
+  // Returns whether there was local state.
+  bool EraseDataForNotificationId(const std::string& notification_id);
+
+  // Closes the notification identified by |notification_id|. Returns whether a
+  // notification was closed in response to the call.
+  bool Close(const std::string& notification_id);
+
+  // KeyedService overrides:
+  void Shutdown() override;
+
+ private:
+  using NotificationVector = std::vector<std::unique_ptr<Notification>>;
+
+  // Returns the notification display service instance to communicate with.
+  NotificationDisplayService* GetDisplayService();
+
+  // The Profile instance that owns this keyed service.
+  Profile* profile_;
+
+  // Vector of notifications that are being shown for extensions.
+  NotificationVector notifications_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionNotificationDisplayHelper);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_H_
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc b/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc
new file mode 100644
index 0000000..e537ef7
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
+
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace extensions {
+
+// static
+ExtensionNotificationDisplayHelperFactory*
+ExtensionNotificationDisplayHelperFactory::GetInstance() {
+  return base::Singleton<ExtensionNotificationDisplayHelperFactory>::get();
+}
+
+// static
+ExtensionNotificationDisplayHelper*
+ExtensionNotificationDisplayHelperFactory::GetForProfile(Profile* profile) {
+  return static_cast<ExtensionNotificationDisplayHelper*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true /* create */));
+}
+
+ExtensionNotificationDisplayHelperFactory::
+    ExtensionNotificationDisplayHelperFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ExtensionNotificationDisplayHelperFactory",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+ExtensionNotificationDisplayHelperFactory::
+    ~ExtensionNotificationDisplayHelperFactory() {}
+
+KeyedService*
+ExtensionNotificationDisplayHelperFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new ExtensionNotificationDisplayHelper(profile);
+}
+
+content::BrowserContext*
+ExtensionNotificationDisplayHelperFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h b/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h
new file mode 100644
index 0000000..8591158
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_FACTORY_H_
+#define CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace extensions {
+
+class ExtensionNotificationDisplayHelper;
+
+class ExtensionNotificationDisplayHelperFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  // Get the singleton instance of the factory.
+  static ExtensionNotificationDisplayHelperFactory* GetInstance();
+
+  // Get the display helper for |profile|, creating one if needed.
+  static ExtensionNotificationDisplayHelper* GetForProfile(Profile* profile);
+
+ protected:
+  // Overridden from BrowserContextKeyedServiceFactory.
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      ExtensionNotificationDisplayHelperFactory>;
+
+  ExtensionNotificationDisplayHelperFactory();
+  ~ExtensionNotificationDisplayHelperFactory() override;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionNotificationDisplayHelperFactory);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_DISPLAY_HELPER_FACTORY_H_
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
new file mode 100644
index 0000000..f5578ff
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/notifications/extension_notification_handler.h"
+
+#include "base/logging.h"
+
+ExtensionNotificationHandler::ExtensionNotificationHandler() = default;
+
+ExtensionNotificationHandler::~ExtensionNotificationHandler() = default;
+
+void ExtensionNotificationHandler::OpenSettings(Profile* profile) {
+  // Extension notifications don't display a settings button.
+  NOTREACHED();
+}
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.h b/chrome/browser/extensions/api/notifications/extension_notification_handler.h
new file mode 100644
index 0000000..9d6985ff
--- /dev/null
+++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_HANDLER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/non_persistent_notification_handler.h"
+
+// Handler for notifications shown by extensions. Will be created and owned by
+// the NotificationDisplayService.
+class ExtensionNotificationHandler : public NonPersistentNotificationHandler {
+ public:
+  ExtensionNotificationHandler();
+  ~ExtensionNotificationHandler() override;
+
+  // NotificationHandler implementation.
+  void OpenSettings(Profile* profile) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExtensionNotificationHandler);
+};
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_NOTIFICATIONS_EXTENSION_NOTIFICATION_HANDLER_H_
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc
index eac302ae..577aaf7 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -19,9 +19,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
 #include "chrome/browser/notifications/notification.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/notifications/notifier_state_tracker.h"
 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -212,10 +212,14 @@
                            const std::string& id)
       : api_function_(api_function),
         event_router_(EventRouter::Get(profile)),
+        display_helper_(
+            ExtensionNotificationDisplayHelperFactory::GetForProfile(profile)),
         extension_id_(extension_id),
         id_(id),
         scoped_id_(CreateScopedIdentifier(extension_id, id)) {
     DCHECK(api_function_);
+    DCHECK(display_helper_);
+
     shutdown_notifier_subscription_ =
         ShutdownNotifierFactory::GetInstance()->Get(profile)->Subscribe(
             base::Bind(&NotificationsApiDelegate::Shutdown,
@@ -230,6 +234,9 @@
     args->AppendBoolean(by_user);
     SendEvent(events::NOTIFICATIONS_ON_CLOSED,
               notifications::OnClosed::kEventName, gesture, std::move(args));
+
+    DCHECK(display_helper_);
+    display_helper_->EraseDataForNotificationId(scoped_id_);
   }
 
   void Click() override {
@@ -302,8 +309,9 @@
   }
 
   void Shutdown() {
-    event_router_ = nullptr;
     shutdown_notifier_subscription_.reset();
+    event_router_ = nullptr;
+    display_helper_ = nullptr;
   }
 
   std::unique_ptr<base::ListValue> CreateBaseEventArgs() {
@@ -318,6 +326,7 @@
   // profile-keyed service shutdown events and reset to nullptr at that time,
   // so make sure to check for a valid pointer before use.
   EventRouter* event_router_;
+  ExtensionNotificationDisplayHelper* display_helper_;
 
   const std::string extension_id_;
   const std::string id_;
@@ -496,7 +505,7 @@
   notification.set_never_timeout(options->require_interaction &&
                                  *options->require_interaction);
 
-  g_browser_process->notification_ui_manager()->Add(notification, GetProfile());
+  GetDisplayHelper()->Display(notification);
   return true;
 }
 
@@ -630,8 +639,10 @@
   if (options->is_clickable.get())
     notification->set_clickable(*options->is_clickable);
 
-  g_browser_process->notification_ui_manager()->Update(*notification,
-                                                       GetProfile());
+  // It's safe to follow the regular path for adding a new notification as it's
+  // already been verified that there is a notification that can be updated.
+  GetDisplayHelper()->Display(*notification);
+
   return true;
 }
 
@@ -652,6 +663,11 @@
   return false;
 }
 
+ExtensionNotificationDisplayHelper* NotificationsApiFunction::GetDisplayHelper()
+    const {
+  return ExtensionNotificationDisplayHelperFactory::GetForProfile(GetProfile());
+}
+
 bool NotificationsApiFunction::RunAsync() {
   if (IsNotificationsApiAvailable() && IsNotificationsApiEnabled()) {
     return RunNotificationsApi();
@@ -728,9 +744,9 @@
   // We are in update.  If the ID doesn't exist, succeed but call the callback
   // with "false".
   const Notification* matched_notification =
-      g_browser_process->notification_ui_manager()->FindById(
-          CreateScopedIdentifier(extension_->id(), params_->notification_id),
-          NotificationUIManager::GetProfileID(GetProfile()));
+      GetDisplayHelper()->GetByNotificationId(
+          CreateScopedIdentifier(extension_->id(), params_->notification_id));
+
   if (!matched_notification) {
     SetResult(base::MakeUnique<base::Value>(false));
     SendResponse(true);
@@ -766,9 +782,8 @@
   params_ = api::notifications::Clear::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
 
-  bool cancel_result = g_browser_process->notification_ui_manager()->CancelById(
-      CreateScopedIdentifier(extension_->id(), params_->notification_id),
-      NotificationUIManager::GetProfileID(GetProfile()));
+  bool cancel_result = GetDisplayHelper()->Close(
+      CreateScopedIdentifier(extension_->id(), params_->notification_id));
 
   SetResult(base::MakeUnique<base::Value>(cancel_result));
   SendResponse(true);
@@ -781,11 +796,8 @@
 NotificationsGetAllFunction::~NotificationsGetAllFunction() {}
 
 bool NotificationsGetAllFunction::RunNotificationsApi() {
-  NotificationUIManager* notification_ui_manager =
-      g_browser_process->notification_ui_manager();
   std::set<std::string> notification_ids =
-      notification_ui_manager->GetAllIdsByProfileAndSourceOrigin(
-          NotificationUIManager::GetProfileID(GetProfile()), extension_->url());
+      GetDisplayHelper()->GetNotificationIdsForExtension(extension_->url());
 
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
 
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.h b/chrome/browser/extensions/api/notifications/notifications_api.h
index d7bb4b7..1a21c11 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.h
+++ b/chrome/browser/extensions/api/notifications/notifications_api.h
@@ -20,6 +20,8 @@
 
 extern const base::Feature kAllowFullscreenAppNotificationsFeature;
 
+class ExtensionNotificationDisplayHelper;
+
 class NotificationsApiFunction : public ChromeAsyncExtensionFunction {
  public:
   // Whether the current extension and channel allow the API. Public for
@@ -40,6 +42,10 @@
 
   bool AreExtensionNotificationsAllowed() const;
 
+  // Returns the display helper that should be used for interacting with the
+  // common notification system.
+  ExtensionNotificationDisplayHelper* GetDisplayHelper() const;
+
   // Returns true if the API function is still allowed to run even when the
   // notifications for a notifier have been disabled.
   virtual bool CanRunWhileDisabled() const;
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 1ebdf8a..761c413 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -8,12 +8,17 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_browsertest_util.h"
-#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
+#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/notifications/notification.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/notifications/notification_common.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/notifications/notifier_state_tracker.h"
+#include "chrome/browser/notifications/notifier_state_tracker_factory.h"
+#include "chrome/browser/notifications/stub_notification_display_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
@@ -30,8 +35,6 @@
 #include "extensions/common/test_util.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification_list.h"
 #include "ui/message_center/notifier_settings.h"
 
 #if defined(OS_MACOSX)
@@ -42,6 +45,8 @@
 using extensions::AppWindow;
 using extensions::AppWindowRegistry;
 using extensions::Extension;
+using extensions::ExtensionNotificationDisplayHelper;
+using extensions::ExtensionNotificationDisplayHelperFactory;
 using extensions::ResultCatcher;
 
 namespace utils = extension_function_test_utils;
@@ -155,14 +160,46 @@
     return NULL;
   }
 
+  ExtensionNotificationDisplayHelper* GetDisplayHelper() {
+    return ExtensionNotificationDisplayHelperFactory::GetForProfile(profile());
+  }
+
+  StubNotificationDisplayService* GetDisplayService() {
+    return reinterpret_cast<StubNotificationDisplayService*>(
+        NotificationDisplayServiceFactory::GetForProfile(profile()));
+  }
+
+  NotifierStateTracker* GetNotifierStateTracker() {
+    return NotifierStateTrackerFactory::GetForProfile(profile());
+  }
+
  protected:
+  void SetUpOnMainThread() override {
+    ExtensionApiTest::SetUpOnMainThread();
+
+    DCHECK(profile());
+    NotificationDisplayServiceFactory::GetInstance()->SetTestingFactory(
+        profile(), &StubNotificationDisplayService::FactoryForTests);
+  }
+
+  // Returns the notification that's being displayed for |extension|, or nullptr
+  // when the notification count is not equal to one. It's not safe to rely on
+  // the Notification pointer after closing the notification, but a copy can be
+  // made to continue to be able to access the underlying information.
+  Notification* GetNotificationForExtension(
+      const extensions::Extension* extension) {
+    DCHECK(extension);
+
+    std::set<std::string> notifications =
+        GetDisplayHelper()->GetNotificationIdsForExtension(extension->url());
+    if (notifications.size() != 1)
+      return nullptr;
+
+    return GetDisplayHelper()->GetByNotificationId(*notifications.begin());
+  }
+
   std::string GetNotificationIdFromDelegateId(const std::string& delegate_id) {
-    return g_browser_process->notification_ui_manager()
-        ->FindById(
-              delegate_id,
-              NotificationUIManager::GetProfileID(
-                  g_browser_process->profile_manager()->GetLastUsedProfile()))
-        ->id();
+    return GetDisplayHelper()->GetByNotificationId(delegate_id)->id();
   }
 
   void LaunchPlatformApp(const Extension* extension) {
@@ -216,8 +253,8 @@
     ResultCatcher catcher;
     const std::string notification_id =
         GetNotificationIdFromDelegateId(extension->id() + "-FOO");
-    g_browser_process->message_center()->RemoveNotification(notification_id,
-                                                            false);
+    GetDisplayService()->RemoveNotification(
+        NotificationCommon::EXTENSION, notification_id, false /* by_user */);
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
 
@@ -225,21 +262,21 @@
     ResultCatcher catcher;
     const std::string notification_id =
         GetNotificationIdFromDelegateId(extension->id() + "-BAR");
-    g_browser_process->message_center()->RemoveNotification(notification_id,
-                                                            true);
+    GetDisplayService()->RemoveNotification(
+        NotificationCommon::EXTENSION, notification_id, true /* by_user */);
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
 
   {
     ResultCatcher catcher;
-    g_browser_process->message_center()->RemoveAllNotifications(
-        false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
+    GetDisplayService()->RemoveAllNotifications(NotificationCommon::EXTENSION,
+                                                false /* by_user */);
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
   {
     ResultCatcher catcher;
-    g_browser_process->message_center()->RemoveAllNotifications(
-        true /* by_user */, message_center::MessageCenter::RemoveType::ALL);
+    GetDisplayService()->RemoveAllNotifications(NotificationCommon::EXTENSION,
+                                                true /* by_user */);
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
 }
@@ -254,11 +291,8 @@
   int kNewPriority = 2;
   const char kButtonTitle[] = "NewButton";
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
-  ASSERT_EQ(extension->url(), notification->origin_url());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
 
   LOG(INFO) << "Notification ID: " << notification->id();
 
@@ -303,9 +337,7 @@
     message_center::NotifierId notifier_id(
         message_center::NotifierId::APPLICATION,
         empty_extension->id());
-    message_center::Notifier notifier(notifier_id, base::string16(), true);
-    g_browser_process->message_center()->GetNotifierSettingsProvider()->
-        SetNotifierEnabled(notifier, false);
+    GetNotifierStateTracker()->SetNotifierEnabled(notifier_id, false);
 
     std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
         notification_function.get(), "[]", browser(), utils::NONE));
@@ -329,9 +361,7 @@
     message_center::NotifierId notifier_id(
         message_center::NotifierId::APPLICATION,
         extension->id());
-    message_center::Notifier notifier(notifier_id, base::string16(), true);
-    g_browser_process->message_center()->GetNotifierSettingsProvider()->
-        SetNotifierEnabled(notifier, false);
+    GetNotifierStateTracker()->SetNotifierEnabled(notifier_id, false);
 
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
@@ -343,9 +373,7 @@
     message_center::NotifierId notifier_id(
         message_center::NotifierId::APPLICATION,
         extension->id());
-    message_center::Notifier notifier(notifier_id, base::string16(), false);
-    g_browser_process->message_center()->GetNotifierSettingsProvider()->
-        SetNotifierEnabled(notifier, true);
+    GetNotifierStateTracker()->SetNotifierEnabled(notifier_id, true);
 
     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
   }
@@ -356,11 +384,8 @@
       LoadExtensionAndWait("notifications/api/user_gesture");
   ASSERT_TRUE(extension) << message_;
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
-  ASSERT_EQ(extension->url(), notification->origin_url());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
 
   {
     UserGestureCatcher catcher;
@@ -368,11 +393,13 @@
     EXPECT_TRUE(catcher.GetNextResult());
     notification->Click();
     EXPECT_TRUE(catcher.GetNextResult());
-    notification->Close(true);
+    notification->Close(true /* by_user */);
     EXPECT_TRUE(catcher.GetNextResult());
-    notification->Close(false);
-    EXPECT_FALSE(catcher.GetNextResult());
+
+    // Note that |notification| no longer points to valid memory.
   }
+
+  ASSERT_FALSE(GetNotificationForExtension(extension));
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestRequireInteraction) {
@@ -380,11 +407,8 @@
       LoadExtensionAndWait("notifications/api/require_interaction");
   ASSERT_TRUE(extension) << message_;
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
-  ASSERT_EQ(extension->url(), notification->origin_url());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
 
   EXPECT_TRUE(notification->never_timeout());
 }
@@ -401,10 +425,9 @@
   ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
       GetFirstAppWindow(extension->id())->GetNativeWindow()));
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
+
   // If the app hasn't created a fullscreen window, then its notifications
   // shouldn't be displayed when a window is fullscreen.
   ASSERT_FALSE(notification->delegate()->ShouldDisplayOverFullscreen());
@@ -430,10 +453,9 @@
   ASSERT_TRUE(GetFirstAppWindow(extension->id())->GetBaseWindow()->IsActive())
       << "Not Active";
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
+
   // If the app has created a fullscreen window, then its notifications should
   // be displayed when a window is fullscreen.
   ASSERT_TRUE(notification->delegate()->ShouldDisplayOverFullscreen());
@@ -459,10 +481,9 @@
   ASSERT_TRUE(GetFirstAppWindow(extension->id())->GetBaseWindow()->IsActive())
       << "Not Active";
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
+
   // When the experiment flag is off, then ShouldDisplayOverFullscreen should
   // return false.
   ASSERT_FALSE(notification->delegate()->ShouldDisplayOverFullscreen());
@@ -493,10 +514,9 @@
   ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
       GetFirstAppWindow(extension2->id())->GetNativeWindow()));
 
-  const message_center::NotificationList::Notifications& notifications =
-      g_browser_process->message_center()->GetVisibleNotifications();
-  ASSERT_EQ(1u, notifications.size());
-  message_center::Notification* notification = *(notifications.begin());
+  Notification* notification = GetNotificationForExtension(extension1);
+  ASSERT_TRUE(notification);
+
   // The first app window is superseded by the second window, so its
   // notification shouldn't be displayed.
   ASSERT_FALSE(notification->delegate()->ShouldDisplayOverFullscreen());
@@ -526,7 +546,10 @@
   ASSERT_TRUE(GetFirstAppWindow(extension->id())->GetBaseWindow()->IsActive())
       << "Not Active";
 
-  const message_center::NotificationList::PopupNotifications notifications =
-      g_browser_process->message_center()->GetPopupNotifications();
-  ASSERT_EQ(1u, notifications.size());
+  Notification* notification = GetNotificationForExtension(extension);
+  ASSERT_TRUE(notification);
+
+  // The extension's window is being shown and focused, so its expected that
+  // the notification displays on top of it.
+  ASSERT_TRUE(notification->delegate()->ShouldDisplayOverFullscreen());
 }
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index 202f84c8..f4333d3 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -299,7 +299,7 @@
   if (allowed_list_pref) {
     for (base::ListValue::const_iterator it = allowed_list_pref->begin();
          it != allowed_list_pref->end(); ++it) {
-      if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
+      if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
         AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
     }
   }
@@ -307,7 +307,7 @@
   if (denied_list_pref) {
     for (base::ListValue::const_iterator it = denied_list_pref->begin();
          it != denied_list_pref->end(); ++it) {
-      if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
+      if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
         AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
     }
   }
@@ -320,7 +320,7 @@
     for (base::ListValue::const_iterator it = install_sources_pref->begin();
          it != install_sources_pref->end(); ++it) {
       std::string url_pattern;
-      if ((*it)->GetAsString(&url_pattern)) {
+      if (it->GetAsString(&url_pattern)) {
         URLPattern entry(URLPattern::SCHEME_ALL);
         if (entry.Parse(url_pattern) == URLPattern::PARSE_SUCCESS) {
           global_settings_->install_sources.AddPattern(entry);
@@ -339,11 +339,11 @@
          it != allowed_types_pref->end(); ++it) {
       int int_value;
       std::string string_value;
-      if ((*it)->GetAsInteger(&int_value) && int_value >= 0 &&
+      if (it->GetAsInteger(&int_value) && int_value >= 0 &&
           int_value < Manifest::Type::NUM_LOAD_TYPES) {
         global_settings_->allowed_types.push_back(
             static_cast<Manifest::Type>(int_value));
-      } else if ((*it)->GetAsString(&string_value)) {
+      } else if (it->GetAsString(&string_value)) {
         Manifest::Type manifest_type =
             schema_constants::GetManifestType(string_value);
         if (manifest_type != Manifest::TYPE_UNKNOWN)
diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc
index 21ce6d0..1f9d817a 100644
--- a/chrome/browser/extensions/extension_override_apitest.cc
+++ b/chrome/browser/extensions/extension_override_apitest.cc
@@ -50,7 +50,7 @@
     for (const auto& val : *values) {
       const base::DictionaryValue* dict = nullptr;
       std::string entry;
-      if (!val->GetAsDictionary(&dict) || !dict->GetString("entry", &entry) ||
+      if (!val.GetAsDictionary(&dict) || !dict->GetString("entry", &entry) ||
           seen_overrides.count(entry) != 0)
         return false;
       seen_overrides.insert(entry);
diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc
index 8ee464b8..16626c3c 100644
--- a/chrome/browser/extensions/extension_system_impl.cc
+++ b/chrome/browser/extensions/extension_system_impl.cc
@@ -243,8 +243,10 @@
 
   bool skip_session_extensions = false;
 #if defined(OS_CHROMEOS)
-  // Skip loading session extensions if we are not in a user session.
-  skip_session_extensions = !chromeos::LoginState::Get()->IsUserLoggedIn();
+  // Skip loading session extensions if we are not in a user session or if the
+  // profile is the sign-in profile, which doesn't correspond to a user session.
+  skip_session_extensions = !chromeos::LoginState::Get()->IsUserLoggedIn() ||
+                            chromeos::ProfileHelper::IsSigninProfile(profile_);
   if (chrome::IsRunningInForcedAppMode()) {
     extension_service_->component_loader()->
         AddDefaultComponentExtensionsForKioskMode(skip_session_extensions);
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index 9ebeb1c5..b7cceb7f1 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -67,17 +67,17 @@
 void InitializeOverridesList(base::ListValue* list) {
   base::ListValue migrated;
   std::set<std::string> seen_entries;
-  for (const auto& val : *list) {
+  for (auto& val : *list) {
     std::unique_ptr<base::DictionaryValue> new_dict(
         new base::DictionaryValue());
     std::string entry_name;
     base::DictionaryValue* existing_dict = nullptr;
-    if (val->GetAsDictionary(&existing_dict)) {
+    if (val.GetAsDictionary(&existing_dict)) {
       bool success = existing_dict->GetString(kEntry, &entry_name);
       if (!success)  // See comment about CHECK(success) in ForEachOverrideList.
         continue;
       new_dict->Swap(existing_dict);
-    } else if (val->GetAsString(&entry_name)) {
+    } else if (val.GetAsString(&entry_name)) {
       new_dict->SetString(kEntry, entry_name);
       new_dict->SetBoolean(kActive, true);
     } else {
@@ -98,10 +98,10 @@
 // marks it as active.
 void AddOverridesToList(base::ListValue* list,
                         const std::string& override) {
-  for (const auto& val : *list) {
+  for (auto& val : *list) {
     base::DictionaryValue* dict = nullptr;
     std::string entry;
-    if (!val->GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
+    if (!val.GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
       NOTREACHED();
       continue;
     }
@@ -123,10 +123,10 @@
 void ValidateOverridesList(const extensions::ExtensionSet* all_extensions,
                            base::ListValue* list) {
   base::ListValue migrated;
-  for (const auto& val : *list) {
+  for (auto& val : *list) {
     base::DictionaryValue* dict = nullptr;
     std::string entry;
-    if (!val->GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
+    if (!val.GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
       NOTREACHED();
       continue;
     }
@@ -177,20 +177,19 @@
 bool UpdateOverridesList(base::ListValue* overrides_list,
                          const std::string& override_url,
                          UpdateBehavior behavior) {
-  base::ListValue::iterator iter =
-      std::find_if(overrides_list->begin(), overrides_list->end(),
-                   [&override_url](const std::unique_ptr<base::Value>& value) {
-                     std::string entry;
-                     const base::DictionaryValue* dict = nullptr;
-                     return value->GetAsDictionary(&dict) &&
-                            dict->GetString(kEntry, &entry) &&
-                            entry == override_url;
-                   });
+  base::ListValue::iterator iter = std::find_if(
+      overrides_list->begin(), overrides_list->end(),
+      [&override_url](const base::Value& value) {
+        std::string entry;
+        const base::DictionaryValue* dict = nullptr;
+        return value.GetAsDictionary(&dict) &&
+               dict->GetString(kEntry, &entry) && entry == override_url;
+      });
   if (iter != overrides_list->end()) {
     switch (behavior) {
       case UPDATE_DEACTIVATE: {
         base::DictionaryValue* dict = nullptr;
-        bool success = (*iter)->GetAsDictionary(&dict);
+        bool success = iter->GetAsDictionary(&dict);
         // See comment about CHECK(success) in ForEachOverrideList.
         if (success) {
           dict->SetBoolean(kActive, false);
@@ -428,7 +427,7 @@
     for (base::ListValue::const_iterator list_iter = url_list->begin();
          list_iter != url_list->end(); ++list_iter) {
       const base::DictionaryValue* dict = nullptr;
-      if (!(*list_iter)->GetAsDictionary(&dict))
+      if (!list_iter->GetAsDictionary(&dict))
         continue;
       std::string override;
       if (!dict->GetString(kEntry, &override))
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
index d96ad63..b73da46 100644
--- a/chrome/browser/extensions/install_signer.cc
+++ b/chrome/browser/extensions/install_signer.cc
@@ -146,7 +146,7 @@
        i != id_list->end();
        ++i) {
     std::string id;
-    if (!(*i)->GetAsString(&id)) {
+    if (!i->GetAsString(&id)) {
       return false;
     }
     ids->insert(id);
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc
index dc1abda..7bfaf2d 100644
--- a/chrome/browser/extensions/menu_manager.cc
+++ b/chrome/browser/extensions/menu_manager.cc
@@ -625,7 +625,6 @@
   if (item->type() == MenuItem::RADIO)
     RadioItemSelected(item);
 
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
 
   std::unique_ptr<base::DictionaryValue> properties(
       new base::DictionaryValue());
@@ -664,8 +663,14 @@
                            webview_guest->view_instance_id());
   }
 
-  base::DictionaryValue* raw_properties = properties.get();
+  auto args = base::MakeUnique<base::ListValue>();
+  args->Reserve(2);
   args->Append(std::move(properties));
+  // |properties| is invalidated at this time, which is why |args| needs to be
+  // queried for the pointer. The obtained pointer is guaranteed to stay valid
+  // even after further Appends, because enough storage was reserved above.
+  base::DictionaryValue* raw_properties = nullptr;
+  args->GetDictionary(0, &raw_properties);
 
   // Add the tab info to the argument list.
   // No tab info in a platform app.
diff --git a/chrome/browser/extensions/policy_handlers.cc b/chrome/browser/extensions/policy_handlers.cc
index 8ee0bee3..0b0b2bf 100644
--- a/chrome/browser/extensions/policy_handlers.cc
+++ b/chrome/browser/extensions/policy_handlers.cc
@@ -78,7 +78,7 @@
   for (base::ListValue::const_iterator entry(list_value->begin());
        entry != list_value->end(); ++entry) {
     std::string id;
-    if (!(*entry)->GetAsString(&id)) {
+    if (!entry->GetAsString(&id)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::STRING));
@@ -143,7 +143,7 @@
   for (base::ListValue::const_iterator entry(policy_list_value->begin());
        entry != policy_list_value->end(); ++entry) {
     std::string entry_string;
-    if (!(*entry)->GetAsString(&entry_string)) {
+    if (!entry->GetAsString(&entry_string)) {
       if (errors) {
         errors->AddError(policy_name(), entry - policy_list_value->begin(),
                          IDS_POLICY_TYPE_ERROR,
@@ -230,7 +230,7 @@
   for (base::ListValue::const_iterator entry(list_value->begin());
        entry != list_value->end(); ++entry) {
     std::string url_pattern_string;
-    if (!(*entry)->GetAsString(&url_pattern_string)) {
+    if (!entry->GetAsString(&url_pattern_string)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::STRING));
diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc
index d675371..52f23c2 100644
--- a/chrome/browser/extensions/webstore_inline_installer.cc
+++ b/chrome/browser/extensions/webstore_inline_installer.cc
@@ -84,7 +84,7 @@
     for (base::ListValue::const_iterator it = verified_sites->begin();
          it != verified_sites->end() && !requestor_is_ok; ++it) {
       std::string verified_site;
-      if (!(*it)->GetAsString(&verified_site)) {
+      if (!it->GetAsString(&verified_site)) {
         *error = kInvalidWebstoreResponseError;
         return false;
       }
diff --git a/chrome/browser/extensions/webstore_inline_installer_browsertest.cc b/chrome/browser/extensions/webstore_inline_installer_browsertest.cc
index 555ef2f..7c3702f 100644
--- a/chrome/browser/extensions/webstore_inline_installer_browsertest.cc
+++ b/chrome/browser/extensions/webstore_inline_installer_browsertest.cc
@@ -434,7 +434,7 @@
   int i = 0;
   for (const auto& value : *redirect_list) {
     std::string value_string;
-    ASSERT_TRUE(value->GetAsString(&value_string));
+    ASSERT_TRUE(value.GetAsString(&value_string));
     GURL redirect_url(value_string);
     EXPECT_EQ(expected_redirect_domains[i++], redirect_url.host());
   }
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 68e086d4..886b108 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -173,7 +173,7 @@
   for (base::ListValue::iterator it = details->begin();
       it != details->end(); ++it) {
     base::DictionaryValue* dict = NULL;
-    if ((*it)->GetAsDictionary(&dict)) {
+    if (it->GetAsDictionary(&dict)) {
       std::string title;
       dict->GetString("title", &title);
       if (title == syncer::sync_ui_util::kIdentityTitle) {
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc
index f43dab5..a971721 100644
--- a/chrome/browser/media/android/router/media_router_android.cc
+++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -6,11 +6,7 @@
 
 #include <string>
 #include <utility>
-#include <vector>
 
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
-#include "base/android/jni_string.h"
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -21,15 +17,8 @@
 #include "chrome/browser/media/router/route_message_observer.h"
 #include "chrome/browser/media/router/route_request_result.h"
 #include "content/public/browser/browser_context.h"
-#include "jni/ChromeMediaRouter_jni.h"
 #include "url/gurl.h"
 
-using base::android::ConvertUTF8ToJavaString;
-using base::android::ConvertJavaStringToUTF8;
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-using base::android::AttachCurrentThread;
-
 namespace media_router {
 
 MediaRouterAndroid::MediaRouteRequest::MediaRouteRequest(
@@ -42,23 +31,12 @@
 
 MediaRouterAndroid::MediaRouteRequest::~MediaRouteRequest() {}
 
-
-MediaRouterAndroid::MediaRouterAndroid(content::BrowserContext*) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  java_media_router_.Reset(
-      Java_ChromeMediaRouter_create(env, reinterpret_cast<jlong>(this)));
-}
+MediaRouterAndroid::MediaRouterAndroid(content::BrowserContext*)
+    : bridge_(new MediaRouterAndroidBridge(this)) {}
 
 MediaRouterAndroid::~MediaRouterAndroid() {
 }
 
-// static
-bool MediaRouterAndroid::Register(JNIEnv* env) {
-  bool ret = RegisterNativesImpl(env);
-  DCHECK(g_ChromeMediaRouter_clazz);
-  return ret;
-}
-
 const MediaRoute* MediaRouterAndroid::FindRouteBySource(
     const MediaSource::Id& source_id) const {
   for (const auto& route : active_routes_) {
@@ -91,22 +69,8 @@
   int route_request_id =
       route_requests_.Add(base::MakeUnique<MediaRouteRequest>(
           MediaSource(source_id), presentation_id, callbacks));
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jsource_id =
-          base::android::ConvertUTF8ToJavaString(env, source_id);
-  ScopedJavaLocalRef<jstring> jsink_id =
-          base::android::ConvertUTF8ToJavaString(env, sink_id);
-  ScopedJavaLocalRef<jstring> jpresentation_id =
-          base::android::ConvertUTF8ToJavaString(env, presentation_id);
-  // TODO(crbug.com/685358): Unique origins should not be considered
-  // same-origin.
-  ScopedJavaLocalRef<jstring> jorigin =
-      base::android::ConvertUTF8ToJavaString(env, origin.GetURL().spec());
-
-  Java_ChromeMediaRouter_createRoute(env, java_media_router_, jsource_id,
-                                     jsink_id, jpresentation_id, jorigin,
-                                     tab_id, is_incognito, route_request_id);
+  bridge_->CreateRoute(source_id, sink_id, presentation_id, origin, tab_id,
+                       is_incognito, route_request_id);
 }
 
 void MediaRouterAndroid::ConnectRouteByRouteId(
@@ -140,25 +104,11 @@
 
   int request_id = route_requests_.Add(base::MakeUnique<MediaRouteRequest>(
       MediaSource(source_id), presentation_id, callbacks));
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jsource_id =
-          base::android::ConvertUTF8ToJavaString(env, source_id);
-  ScopedJavaLocalRef<jstring> jpresentation_id =
-          base::android::ConvertUTF8ToJavaString(env, presentation_id);
-  ScopedJavaLocalRef<jstring> jorigin =
-      base::android::ConvertUTF8ToJavaString(env, origin.GetURL().spec());
-
-  Java_ChromeMediaRouter_joinRoute(env, java_media_router_, jsource_id,
-                                   jpresentation_id, jorigin, tab_id,
-                                   request_id);
+  bridge_->JoinRoute(source_id, presentation_id, origin, tab_id, request_id);
 }
 
 void MediaRouterAndroid::TerminateRoute(const MediaRoute::Id& route_id) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jroute_id =
-          base::android::ConvertUTF8ToJavaString(env, route_id);
-  Java_ChromeMediaRouter_closeRoute(env, java_media_router_, jroute_id);
+  bridge_->TerminateRoute(route_id);
 }
 
 void MediaRouterAndroid::SendRouteMessage(
@@ -167,13 +117,7 @@
     const SendRouteMessageCallback& callback) {
   int callback_id = message_callbacks_.Add(
       base::MakeUnique<SendRouteMessageCallback>(callback));
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jroute_id =
-          base::android::ConvertUTF8ToJavaString(env, route_id);
-  ScopedJavaLocalRef<jstring> jmessage =
-          base::android::ConvertUTF8ToJavaString(env, message);
-  Java_ChromeMediaRouter_sendStringMessage(env, java_media_router_, jroute_id,
-                                           jmessage, callback_id);
+  bridge_->SendRouteMessage(route_id, message, callback_id);
 }
 
 void MediaRouterAndroid::SendRouteBinaryMessage(
@@ -211,10 +155,11 @@
 }
 
 void MediaRouterAndroid::DetachRoute(const MediaRoute::Id& route_id) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jroute_id =
-          base::android::ConvertUTF8ToJavaString(env, route_id);
-  Java_ChromeMediaRouter_detachRoute(env, java_media_router_, jroute_id);
+  bridge_->DetachRoute(route_id);
+  RemoveRoute(route_id);
+  NotifyPresentationConnectionClose(
+      route_id, content::PRESENTATION_CONNECTION_CLOSE_REASON_CLOSED,
+      "Remove route");
 }
 
 bool MediaRouterAndroid::RegisterMediaSinksObserver(
@@ -228,11 +173,7 @@
   }
 
   observer_list->AddObserver(observer);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jsource_id =
-      base::android::ConvertUTF8ToJavaString(env, source_id);
-  return Java_ChromeMediaRouter_startObservingMediaSinks(
-      env, java_media_router_, jsource_id);
+  return bridge_->StartObservingMediaSinks(source_id);
 }
 
 void MediaRouterAndroid::UnregisterMediaSinksObserver(
@@ -249,11 +190,7 @@
   it->second->RemoveObserver(observer);
   if (!it->second->might_have_observers()) {
     sinks_observers_.erase(source_id);
-    JNIEnv* env = base::android::AttachCurrentThread();
-    ScopedJavaLocalRef<jstring> jsource_id =
-        base::android::ConvertUTF8ToJavaString(env, source_id);
-    Java_ChromeMediaRouter_stopObservingMediaSinks(env, java_media_router_,
-                                                   jsource_id);
+    bridge_->StopObservingMediaSinks(source_id);
   }
 }
 
@@ -306,48 +243,26 @@
     message_observers_.erase(route_id);
 }
 
-void MediaRouterAndroid::OnSinksReceived(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jsource_urn,
-    jint jcount) {
-  std::vector<MediaSink> sinks_converted;
-  sinks_converted.reserve(jcount);
-  for (int i = 0; i < jcount; ++i) {
-    ScopedJavaLocalRef<jstring> jsink_urn = Java_ChromeMediaRouter_getSinkUrn(
-        env, java_media_router_, jsource_urn, i);
-    ScopedJavaLocalRef<jstring> jsink_name = Java_ChromeMediaRouter_getSinkName(
-        env, java_media_router_, jsource_urn, i);
-    sinks_converted.push_back(
-        MediaSink(ConvertJavaStringToUTF8(env, jsink_urn.obj()),
-        ConvertJavaStringToUTF8(env, jsink_name.obj()),
-        MediaSink::GENERIC));
-  }
-
-  std::string source_urn = ConvertJavaStringToUTF8(env, jsource_urn);
+void MediaRouterAndroid::OnSinksReceived(const std::string& source_urn,
+                                         const std::vector<MediaSink>& sinks) {
   auto it = sinks_observers_.find(source_urn);
   if (it != sinks_observers_.end()) {
     // TODO(imcheng): Pass origins to OnSinksUpdated (crbug.com/594858).
     for (auto& observer : *it->second)
-      observer.OnSinksUpdated(sinks_converted, std::vector<url::Origin>());
+      observer.OnSinksUpdated(sinks, std::vector<url::Origin>());
   }
 }
 
-void MediaRouterAndroid::OnRouteCreated(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jmedia_route_id,
-    const JavaParamRef<jstring>& jsink_id,
-    jint jroute_request_id,
-    jboolean jis_local) {
-  MediaRouteRequest* request = route_requests_.Lookup(jroute_request_id);
+void MediaRouterAndroid::OnRouteCreated(const MediaRoute::Id& route_id,
+                                        const MediaSink::Id& sink_id,
+                                        int route_request_id,
+                                        bool is_local) {
+  MediaRouteRequest* request = route_requests_.Lookup(route_request_id);
   if (!request)
     return;
 
-  MediaRoute route(ConvertJavaStringToUTF8(env, jmedia_route_id),
-                   request->media_source,
-                   ConvertJavaStringToUTF8(env, jsink_id), std::string(),
-                   jis_local, std::string(),
+  MediaRoute route(route_id, request->media_source, sink_id, std::string(),
+                   is_local, std::string(),
                    true);  // TODO(avayvod): Populate for_display.
 
   std::unique_ptr<RouteRequestResult> result =
@@ -355,37 +270,63 @@
   for (const MediaRouteResponseCallback& callback : request->callbacks)
     callback.Run(*result);
 
-  route_requests_.Remove(jroute_request_id);
+  route_requests_.Remove(route_request_id);
 
   active_routes_.push_back(route);
   for (auto& observer : routes_observers_)
     observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>());
 }
 
-void MediaRouterAndroid::OnRouteRequestError(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jerror_text,
-    jint jroute_request_id) {
-  MediaRouteRequest* request = route_requests_.Lookup(jroute_request_id);
+void MediaRouterAndroid::OnRouteRequestError(const std::string& error_text,
+                                             int route_request_id) {
+  MediaRouteRequest* request = route_requests_.Lookup(route_request_id);
   if (!request)
     return;
 
   // TODO(imcheng): Provide a more specific result code.
-  std::unique_ptr<RouteRequestResult> result =
-      RouteRequestResult::FromError(ConvertJavaStringToUTF8(env, jerror_text),
-                                    RouteRequestResult::UNKNOWN_ERROR);
+  std::unique_ptr<RouteRequestResult> result = RouteRequestResult::FromError(
+      error_text, RouteRequestResult::UNKNOWN_ERROR);
   for (const MediaRouteResponseCallback& callback : request->callbacks)
     callback.Run(*result);
 
-  route_requests_.Remove(jroute_request_id);
+  route_requests_.Remove(route_request_id);
 }
 
-void MediaRouterAndroid::OnRouteClosed(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jmedia_route_id) {
-  MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
+void MediaRouterAndroid::OnRouteClosed(const MediaRoute::Id& route_id) {
+  RemoveRoute(route_id);
+  NotifyPresentationConnectionStateChange(
+      route_id, content::PRESENTATION_CONNECTION_STATE_TERMINATED);
+}
+
+void MediaRouterAndroid::OnRouteClosedWithError(const MediaRoute::Id& route_id,
+                                                const std::string& message) {
+  RemoveRoute(route_id);
+  NotifyPresentationConnectionClose(
+      route_id,
+      content::PRESENTATION_CONNECTION_CLOSE_REASON_CONNECTION_ERROR,
+      message);
+}
+
+void MediaRouterAndroid::OnMessageSentResult(bool success, int callback_id) {
+  SendRouteMessageCallback* callback = message_callbacks_.Lookup(callback_id);
+  callback->Run(success);
+  message_callbacks_.Remove(callback_id);
+}
+
+void MediaRouterAndroid::OnMessage(const MediaRoute::Id& route_id,
+                                   const std::string& message) {
+  auto it = message_observers_.find(route_id);
+  if (it == message_observers_.end())
+    return;
+
+  std::vector<RouteMessage> messages(1);
+  messages.front().type = RouteMessage::TEXT;
+  messages.front().text = message;
+  for (auto& observer : *it->second.get())
+    observer.OnMessagesReceived(messages);
+}
+
+void MediaRouterAndroid::RemoveRoute(const MediaRoute::Id& route_id) {
   for (auto it = active_routes_.begin(); it != active_routes_.end(); ++it)
     if (it->media_route_id() == route_id) {
       active_routes_.erase(it);
@@ -394,49 +335,6 @@
 
   for (auto& observer : routes_observers_)
     observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>());
-  NotifyPresentationConnectionStateChange(
-      route_id, content::PRESENTATION_CONNECTION_STATE_TERMINATED);
-}
-
-void MediaRouterAndroid::OnRouteClosedWithError(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jmedia_route_id,
-    const JavaParamRef<jstring>& jmessage) {
-  MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
-  std::string message = ConvertJavaStringToUTF8(env, jmessage);
-  NotifyPresentationConnectionClose(
-      route_id,
-      content::PRESENTATION_CONNECTION_CLOSE_REASON_CONNECTION_ERROR,
-      message);
-
-  OnRouteClosed(env, obj, jmedia_route_id);
-}
-
-void MediaRouterAndroid::OnMessageSentResult(JNIEnv* env,
-                                             const JavaParamRef<jobject>& obj,
-                                             jboolean jsuccess,
-                                             jint jcallback_id) {
-  SendRouteMessageCallback* callback = message_callbacks_.Lookup(jcallback_id);
-  callback->Run(jsuccess);
-  message_callbacks_.Remove(jcallback_id);
-}
-
-// Notifies the media router about a message received from the media route.
-void MediaRouterAndroid::OnMessage(JNIEnv* env,
-                                   const JavaParamRef<jobject>& obj,
-                                   const JavaParamRef<jstring>& jmedia_route_id,
-                                   const JavaParamRef<jstring>& jmessage) {
-  MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
-  auto it = message_observers_.find(route_id);
-  if (it == message_observers_.end())
-    return;
-
-  std::vector<RouteMessage> messages(1);
-  messages.front().type = RouteMessage::TEXT;
-  messages.front().text = ConvertJavaStringToUTF8(env, jmessage);
-  for (auto& observer : *it->second.get())
-    observer.OnMessagesReceived(messages);
 }
 
 }  // namespace media_router
diff --git a/chrome/browser/media/android/router/media_router_android.h b/chrome/browser/media/android/router/media_router_android.h
index 523b1f6e..ed887ff 100644
--- a/chrome/browser/media/android/router/media_router_android.h
+++ b/chrome/browser/media/android/router/media_router_android.h
@@ -5,16 +5,15 @@
 #ifndef CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_H_
 #define CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_H_
 
-#include <jni.h>
 #include <stdint.h>
 
 #include <memory>
 #include <unordered_map>
 
-#include "base/android/scoped_java_ref.h"
 #include "base/id_map.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "chrome/browser/media/android/router/media_router_android_bridge.h"
 #include "chrome/browser/media/router/media_router_base.h"
 
 namespace content {
@@ -23,13 +22,11 @@
 
 namespace media_router {
 
-// A stub implementation of MediaRouter interface on Android.
+// An implementation of MediaRouter interface on Android.
 class MediaRouterAndroid : public MediaRouterBase {
  public:
   ~MediaRouterAndroid() override;
 
-  static bool Register(JNIEnv* env);
-
   const MediaRoute* FindRouteBySource(const MediaSource::Id& source_id) const;
 
   // MediaRouter implementation.
@@ -76,61 +73,44 @@
   void ProvideSinks(const std::string& provider_name,
                     const std::vector<MediaSinkInternal>& sinks) override;
 
-  // The methods called by the Java counterpart.
-
+  // The methods called by the Java bridge.
   // Notifies the media router that information about sinks is received for
-  // a specific source URN.
-  void OnSinksReceived(JNIEnv* env,
-                       const base::android::JavaParamRef<jobject>& obj,
-                       const base::android::JavaParamRef<jstring>& jsource_urn,
-                       jint jcount);
+  // a specific source id.
+  void OnSinksReceived(const MediaSource::Id& source_id,
+                       const std::vector<MediaSink>& sinks);
 
   // Notifies the media router about a successful route creation.
-  void OnRouteCreated(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& jmedia_route_id,
-      const base::android::JavaParamRef<jstring>& jmedia_sink_id,
-      jint jroute_request_id,
-      jboolean jis_local);
+  void OnRouteCreated(const MediaRoute::Id& route_id,
+                      const MediaSink::Id& sink_id,
+                      int request_id,
+                      bool is_local);
 
   // Notifies the media router that route creation or joining failed.
-  void OnRouteRequestError(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& jerror_text,
-      jint jroute_request_id);
+  void OnRouteRequestError(const std::string& error_text, int request_id);
 
   // Notifies the media router when the route was closed.
-  void OnRouteClosed(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& jmedia_route_id);
+  void OnRouteClosed(const MediaRoute::Id& route_id);
 
   // Notifies the media router when the route was closed with an error.
-  void OnRouteClosedWithError(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& jmedia_route_id,
-      const base::android::JavaParamRef<jstring>& jmessage);
+  void OnRouteClosedWithError(const MediaRoute::Id& route_id,
+                              const std::string& message);
 
   // Notifies the media router about the result of sending a message.
-  void OnMessageSentResult(JNIEnv* env,
-                           const base::android::JavaParamRef<jobject>& obj,
-                           jboolean jsuccess,
-                           jint jcallback_id);
+  void OnMessageSentResult(bool success, int callback_id);
 
   // Notifies the media router about a message received from the media route.
-  void OnMessage(JNIEnv* env,
-                 const base::android::JavaParamRef<jobject>& obj,
-                 const base::android::JavaParamRef<jstring>& jmedia_route_id,
-                 const base::android::JavaParamRef<jstring>& jmessage);
+  void OnMessage(const MediaRoute::Id& route_id, const std::string& message);
 
  private:
   friend class MediaRouterFactory;
+  friend class MediaRouterAndroidTest;
 
   explicit MediaRouterAndroid(content::BrowserContext*);
 
+  // Removes the route with the given id from |active_routes_| and updates the
+  // registered route observers.
+  void RemoveRoute(const MediaRoute::Id& route_id);
+
   // MediaRouter implementation.
   bool RegisterMediaSinksObserver(MediaSinksObserver* observer) override;
   void UnregisterMediaSinksObserver(MediaSinksObserver* observer) override;
@@ -141,7 +121,11 @@
   void RegisterRouteMessageObserver(RouteMessageObserver* observer) override;
   void UnregisterRouteMessageObserver(RouteMessageObserver* observer) override;
 
-  base::android::ScopedJavaGlobalRef<jobject> java_media_router_;
+  void SetMediaRouterBridgeForTest(MediaRouterAndroidBridge* bridge) {
+    bridge_.reset(bridge);
+  }
+
+  std::unique_ptr<MediaRouterAndroidBridge> bridge_;
 
   using MediaSinkObservers = std::unordered_map<
       MediaSource::Id,
@@ -179,6 +163,6 @@
   DISALLOW_COPY_AND_ASSIGN(MediaRouterAndroid);
 };
 
-} // namespace media_router
+}  // namespace media_router
 
 #endif  // CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_H_
diff --git a/chrome/browser/media/android/router/media_router_android_bridge.cc b/chrome/browser/media/android/router/media_router_android_bridge.cc
new file mode 100644
index 0000000..e56bbb59
--- /dev/null
+++ b/chrome/browser/media/android/router/media_router_android_bridge.cc
@@ -0,0 +1,197 @@
+// 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/media/android/router/media_router_android_bridge.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "chrome/browser/media/android/router/media_router_android.h"
+#include "jni/ChromeMediaRouter_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+using base::android::AttachCurrentThread;
+
+namespace media_router {
+
+MediaRouterAndroidBridge::MediaRouterAndroidBridge(MediaRouterAndroid* router)
+    : native_media_router_(router) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  java_media_router_.Reset(
+      Java_ChromeMediaRouter_create(env, reinterpret_cast<jlong>(this)));
+}
+
+MediaRouterAndroidBridge::~MediaRouterAndroidBridge() = default;
+
+// static
+bool MediaRouterAndroidBridge::Register(JNIEnv* env) {
+  bool ret = RegisterNativesImpl(env);
+  DCHECK(g_ChromeMediaRouter_clazz);
+  return ret;
+}
+
+void MediaRouterAndroidBridge::CreateRoute(const MediaSource::Id& source_id,
+                                           const MediaSink::Id& sink_id,
+                                           const std::string& presentation_id,
+                                           const url::Origin& origin,
+                                           int tab_id,
+                                           bool is_incognito,
+                                           int route_request_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jsource_id =
+      base::android::ConvertUTF8ToJavaString(env, source_id);
+  ScopedJavaLocalRef<jstring> jsink_id =
+      base::android::ConvertUTF8ToJavaString(env, sink_id);
+  ScopedJavaLocalRef<jstring> jpresentation_id =
+      base::android::ConvertUTF8ToJavaString(env, presentation_id);
+  // TODO(crbug.com/685358): Unique origins should not be considered
+  // same-origin.
+  ScopedJavaLocalRef<jstring> jorigin =
+      base::android::ConvertUTF8ToJavaString(env, origin.GetURL().spec());
+
+  Java_ChromeMediaRouter_createRoute(env, java_media_router_, jsource_id,
+                                     jsink_id, jpresentation_id, jorigin,
+                                     tab_id, is_incognito, route_request_id);
+}
+
+void MediaRouterAndroidBridge::JoinRoute(const MediaSource::Id& source_id,
+                                         const std::string& presentation_id,
+                                         const url::Origin& origin,
+                                         int tab_id,
+                                         int route_request_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jsource_id =
+      base::android::ConvertUTF8ToJavaString(env, source_id);
+  ScopedJavaLocalRef<jstring> jpresentation_id =
+      base::android::ConvertUTF8ToJavaString(env, presentation_id);
+  ScopedJavaLocalRef<jstring> jorigin =
+      base::android::ConvertUTF8ToJavaString(env, origin.GetURL().spec());
+
+  Java_ChromeMediaRouter_joinRoute(env, java_media_router_, jsource_id,
+                                   jpresentation_id, jorigin, tab_id,
+                                   route_request_id);
+}
+
+void MediaRouterAndroidBridge::TerminateRoute(const MediaRoute::Id& route_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jroute_id =
+      base::android::ConvertUTF8ToJavaString(env, route_id);
+  Java_ChromeMediaRouter_closeRoute(env, java_media_router_, jroute_id);
+}
+
+void MediaRouterAndroidBridge::SendRouteMessage(const MediaRoute::Id& route_id,
+                                                const std::string& message,
+                                                int callback_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jroute_id =
+      base::android::ConvertUTF8ToJavaString(env, route_id);
+  ScopedJavaLocalRef<jstring> jmessage =
+      base::android::ConvertUTF8ToJavaString(env, message);
+  Java_ChromeMediaRouter_sendStringMessage(env, java_media_router_, jroute_id,
+                                           jmessage, callback_id);
+}
+
+void MediaRouterAndroidBridge::DetachRoute(const MediaRoute::Id& route_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jroute_id =
+      base::android::ConvertUTF8ToJavaString(env, route_id);
+  Java_ChromeMediaRouter_detachRoute(env, java_media_router_, jroute_id);
+}
+
+bool MediaRouterAndroidBridge::StartObservingMediaSinks(
+    const MediaSource::Id& source_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jsource_id =
+      base::android::ConvertUTF8ToJavaString(env, source_id);
+  return Java_ChromeMediaRouter_startObservingMediaSinks(
+      env, java_media_router_, jsource_id);
+}
+
+void MediaRouterAndroidBridge::StopObservingMediaSinks(
+    const MediaSource::Id& source_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jsource_id =
+      base::android::ConvertUTF8ToJavaString(env, source_id);
+  Java_ChromeMediaRouter_stopObservingMediaSinks(env, java_media_router_,
+                                                 jsource_id);
+}
+
+void MediaRouterAndroidBridge::OnSinksReceived(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jsource_urn,
+    jint jcount) {
+  std::vector<MediaSink> sinks_converted;
+  sinks_converted.reserve(jcount);
+  for (int i = 0; i < jcount; ++i) {
+    ScopedJavaLocalRef<jstring> jsink_urn = Java_ChromeMediaRouter_getSinkUrn(
+        env, java_media_router_, jsource_urn, i);
+    ScopedJavaLocalRef<jstring> jsink_name = Java_ChromeMediaRouter_getSinkName(
+        env, java_media_router_, jsource_urn, i);
+    sinks_converted.push_back(MediaSink(
+        ConvertJavaStringToUTF8(env, jsink_urn.obj()),
+        ConvertJavaStringToUTF8(env, jsink_name.obj()), MediaSink::GENERIC));
+  }
+  native_media_router_->OnSinksReceived(
+      ConvertJavaStringToUTF8(env, jsource_urn), sinks_converted);
+}
+
+void MediaRouterAndroidBridge::OnRouteCreated(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jmedia_route_id,
+    const JavaRef<jstring>& jsink_id,
+    jint jroute_request_id,
+    jboolean jis_local) {
+  native_media_router_->OnRouteCreated(
+      ConvertJavaStringToUTF8(env, jmedia_route_id),
+      ConvertJavaStringToUTF8(env, jsink_id), jroute_request_id, jis_local);
+}
+
+void MediaRouterAndroidBridge::OnRouteRequestError(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jerror_text,
+    jint jroute_request_id) {
+  native_media_router_->OnRouteRequestError(
+      ConvertJavaStringToUTF8(jerror_text), jroute_request_id);
+}
+
+void MediaRouterAndroidBridge::OnRouteClosed(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jmedia_route_id) {
+  native_media_router_->OnRouteClosed(
+      ConvertJavaStringToUTF8(env, jmedia_route_id));
+}
+
+void MediaRouterAndroidBridge::OnRouteClosedWithError(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jmedia_route_id,
+    const JavaRef<jstring>& jmessage) {
+  native_media_router_->OnRouteClosedWithError(
+      ConvertJavaStringToUTF8(env, jmedia_route_id),
+      ConvertJavaStringToUTF8(env, jmessage));
+}
+
+void MediaRouterAndroidBridge::OnMessageSentResult(JNIEnv* env,
+                                                   const JavaRef<jobject>& obj,
+                                                   jboolean jsuccess,
+                                                   jint jcallback_id) {
+  native_media_router_->OnMessageSentResult(jsuccess, jcallback_id);
+}
+
+void MediaRouterAndroidBridge::OnMessage(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj,
+    const JavaRef<jstring>& jmedia_route_id,
+    const JavaRef<jstring>& jmessage) {
+  native_media_router_->OnMessage(ConvertJavaStringToUTF8(env, jmedia_route_id),
+                                  ConvertJavaStringToUTF8(env, jmessage));
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/android/router/media_router_android_bridge.h b/chrome/browser/media/android/router/media_router_android_bridge.h
new file mode 100644
index 0000000..9669b3b
--- /dev/null
+++ b/chrome/browser/media/android/router/media_router_android_bridge.h
@@ -0,0 +1,92 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_BRIDGE_H_
+#define CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_BRIDGE_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/media/router/media_route.h"
+#include "chrome/browser/media/router/media_sink.h"
+#include "chrome/browser/media/router/media_source.h"
+#include "url/origin.h"
+
+namespace media_router {
+
+class MediaRouterAndroid;
+
+// Wraps the JNI layer between MediaRouterAndroid and ChromeMediaRouter.
+class MediaRouterAndroidBridge {
+ public:
+  explicit MediaRouterAndroidBridge(MediaRouterAndroid* router);
+  ~MediaRouterAndroidBridge();
+
+  static bool Register(JNIEnv* env);
+
+  // Implement the corresponding calls for the MediaRouterAndroid class.
+  // Virtual so could be overridden by tests.
+  virtual void CreateRoute(const MediaSource::Id& source_id,
+                           const MediaSink::Id& sink_id,
+                           const std::string& presentation_id,
+                           const url::Origin& origin,
+                           int tab_id,
+                           bool is_incognito,
+                           int route_request_id);
+  virtual void JoinRoute(const MediaSource::Id& source_id,
+                         const std::string& presentation_id,
+                         const url::Origin& origin,
+                         int tab_id,
+                         int route_request_id);
+  virtual void TerminateRoute(const MediaRoute::Id& route_id);
+  virtual void SendRouteMessage(const MediaRoute::Id& route_id,
+                                const std::string& message,
+                                int callback_id);
+  virtual void DetachRoute(const MediaRoute::Id& route_id);
+  virtual bool StartObservingMediaSinks(const MediaSource::Id& source_id);
+  virtual void StopObservingMediaSinks(const MediaSource::Id& source_id);
+
+  // Methods called by the Java counterpart.
+  void OnSinksReceived(JNIEnv* env,
+                       const base::android::JavaRef<jobject>& obj,
+                       const base::android::JavaRef<jstring>& jsource_urn,
+                       jint jcount);
+  void OnRouteCreated(JNIEnv* env,
+                      const base::android::JavaRef<jobject>& obj,
+                      const base::android::JavaRef<jstring>& jmedia_route_id,
+                      const base::android::JavaRef<jstring>& jmedia_sink_id,
+                      jint jroute_request_id,
+                      jboolean jis_local);
+  void OnRouteRequestError(JNIEnv* env,
+                           const base::android::JavaRef<jobject>& obj,
+                           const base::android::JavaRef<jstring>& jerror_text,
+                           jint jroute_request_id);
+  void OnRouteClosed(JNIEnv* env,
+                     const base::android::JavaRef<jobject>& obj,
+                     const base::android::JavaRef<jstring>& jmedia_route_id);
+  void OnRouteClosedWithError(
+      JNIEnv* env,
+      const base::android::JavaRef<jobject>& obj,
+      const base::android::JavaRef<jstring>& jmedia_route_id,
+      const base::android::JavaRef<jstring>& jmessage);
+  void OnMessageSentResult(JNIEnv* env,
+                           const base::android::JavaRef<jobject>& obj,
+                           jboolean jsuccess,
+                           jint jcallback_id);
+  void OnMessage(JNIEnv* env,
+                 const base::android::JavaRef<jobject>& obj,
+                 const base::android::JavaRef<jstring>& jmedia_route_id,
+                 const base::android::JavaRef<jstring>& jmessage);
+
+ private:
+  MediaRouterAndroid* native_media_router_;
+  base::android::ScopedJavaGlobalRef<jobject> java_media_router_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaRouterAndroidBridge);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ANDROID_ROUTER_MEDIA_ROUTER_ANDROID_BRIDGE_H_
diff --git a/chrome/browser/media/android/router/media_router_android_unittest.cc b/chrome/browser/media/android/router/media_router_android_unittest.cc
new file mode 100644
index 0000000..5af89ba
--- /dev/null
+++ b/chrome/browser/media/android/router/media_router_android_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/test/mock_callback.h"
+#include "chrome/browser/media/android/router/media_router_android.h"
+#include "chrome/browser/media/android/router/media_router_android_bridge.h"
+#include "chrome/browser/media/router/test_helper.h"
+#include "content/public/browser/presentation_service_delegate.h"
+#include "content/public/common/presentation_info.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
+
+using testing::_;
+using testing::Expectation;
+using testing::Return;
+
+namespace media_router {
+
+class MockMediaRouterAndroidBridge : public MediaRouterAndroidBridge {
+ public:
+  MockMediaRouterAndroidBridge() : MediaRouterAndroidBridge(nullptr) {}
+  ~MockMediaRouterAndroidBridge() = default;
+
+  MOCK_METHOD7(CreateRoute,
+               void(const MediaSource::Id&,
+                    const MediaSink::Id&,
+                    const std::string&,
+                    const url::Origin&,
+                    int,
+                    bool,
+                    int));
+  MOCK_METHOD5(JoinRoute,
+               void(const MediaSource::Id&,
+                    const std::string&,
+                    const url::Origin&,
+                    int,
+                    int));
+  MOCK_METHOD1(TerminateRoute, void(const MediaRoute::Id&));
+  MOCK_METHOD3(SendRouteMessage,
+               void(const MediaRoute::Id&, const std::string&, int));
+  MOCK_METHOD1(DetachRoute, void(const MediaRoute::Id&));
+  MOCK_METHOD1(StartObservingMediaSinks, bool(const MediaSource::Id&));
+  MOCK_METHOD1(StopObservingMediaSinks, void(const MediaSource::Id&));
+};
+
+class MediaRouterAndroidTest : public testing::Test {
+ public:
+  void SetUp() override {
+    mock_bridge_ = new MockMediaRouterAndroidBridge();
+    router_.reset(new MediaRouterAndroid(nullptr));
+    router_->SetMediaRouterBridgeForTest(mock_bridge_);
+  }
+
+ protected:
+  // For the checks that MediaRouter calls are running on the UI thread.
+  // Needs to be the first member variable to be destroyed last.
+  content::TestBrowserThreadBundle thread_bundle_;
+
+  std::unique_ptr<MediaRouterAndroid> router_;
+  MockMediaRouterAndroidBridge* mock_bridge_;
+};
+
+TEST_F(MediaRouterAndroidTest, DetachRoute) {
+  base::MockCallback<content::PresentationConnectionStateChangedCallback>
+      callback;
+  content::PresentationConnectionStateChangeInfo change_info_closed(
+      content::PRESENTATION_CONNECTION_STATE_CLOSED);
+  change_info_closed.close_reason =
+      content::PRESENTATION_CONNECTION_CLOSE_REASON_CLOSED;
+  change_info_closed.message = "Remove route";
+  EXPECT_CALL(callback, Run(StateChangeInfoEquals(change_info_closed)));
+
+  Expectation createRouteExpectation =
+      EXPECT_CALL(*mock_bridge_, CreateRoute(_, _, _, _, _, _, 1))
+          .WillOnce(Return());
+  EXPECT_CALL(*mock_bridge_, DetachRoute("route"))
+      .After(createRouteExpectation)
+      .WillOnce(Return());
+
+  router_->CreateRoute("source", "sink", url::Origin(), nullptr,
+                       std::vector<MediaRouteResponseCallback>(),
+                       base::TimeDelta(), false);
+  router_->OnRouteCreated("route", "sink", 1, false);
+
+  EXPECT_NE(nullptr, router_->FindRouteBySource("source"));
+
+  std::unique_ptr<PresentationConnectionStateSubscription> subscription =
+      router_->AddPresentationConnectionStateChangedCallback("route",
+                                                             callback.Get());
+  router_->DetachRoute("route");
+
+  EXPECT_EQ(nullptr, router_->FindRouteBySource("source"));
+}
+
+TEST_F(MediaRouterAndroidTest, OnRouteClosed) {
+  base::MockCallback<content::PresentationConnectionStateChangedCallback>
+      callback;
+  content::PresentationConnectionStateChangeInfo change_info_terminated(
+      content::PRESENTATION_CONNECTION_STATE_TERMINATED);
+  EXPECT_CALL(callback, Run(StateChangeInfoEquals(change_info_terminated)));
+
+  Expectation createRouteExpectation =
+      EXPECT_CALL(*mock_bridge_, CreateRoute(_, _, _, _, _, _, 1))
+          .WillOnce(Return());
+  EXPECT_CALL(*mock_bridge_, DetachRoute("route"))
+      .After(createRouteExpectation)
+      .WillOnce(Return());
+
+  router_->CreateRoute("source", "sink", url::Origin(), nullptr,
+                       std::vector<MediaRouteResponseCallback>(),
+                       base::TimeDelta(), false);
+  router_->OnRouteCreated("route", "sink", 1, false);
+
+  EXPECT_NE(nullptr, router_->FindRouteBySource("source"));
+
+  std::unique_ptr<PresentationConnectionStateSubscription> subscription =
+      router_->AddPresentationConnectionStateChangedCallback("route",
+                                                             callback.Get());
+  router_->OnRouteClosed("route");
+
+  EXPECT_EQ(nullptr, router_->FindRouteBySource("source"));
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/chromeos_login_media_access_handler.cc b/chrome/browser/media/chromeos_login_media_access_handler.cc
index 0b79c970..e413fda 100644
--- a/chrome/browser/media/chromeos_login_media_access_handler.cc
+++ b/chrome/browser/media/chromeos_login_media_access_handler.cc
@@ -63,7 +63,7 @@
   DCHECK(is_list);
   for (const auto& base_value : *list_value) {
     std::string value;
-    if (base_value->GetAsString(&value)) {
+    if (base_value.GetAsString(&value)) {
       const ContentSettingsPattern pattern =
           ContentSettingsPattern::FromString(value);
       // Force administrators to specify more-specific patterns by ignoring the
diff --git a/chrome/browser/media/router/discovery/dial/dial_service.cc b/chrome/browser/media/router/discovery/dial/dial_service.cc
index a03fe0c2..655bc63 100644
--- a/chrome/browser/media/router/discovery/dial/dial_service.cc
+++ b/chrome/browser/media/router/discovery/dial/dial_service.cc
@@ -132,6 +132,23 @@
     bind_address_list->push_back(bind_ip_address);
   }
 }
+
+net::IPAddressList GetBestBindAddressOnUIThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  net::IPAddressList bind_address_list;
+  if (chromeos::NetworkHandler::IsInitialized()) {
+    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::Ethernet(),
+                                  &bind_address_list);
+    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::WiFi(),
+                                  &bind_address_list);
+  } else {
+    VLOG(1) << "InsertBestBindAddressChromeOSOnUIThread called with "
+               "uninitialized NetworkHandler";
+  }
+  return bind_address_list;
+}
+
 #else
 NetworkInterfaceList GetNetworkListOnFileThread() {
   NetworkInterfaceList list;
@@ -433,16 +450,10 @@
   }
 
 #if defined(OS_CHROMEOS)
-  // The ChromeOS specific version of getting network interfaces does not
-  // require trampolining to another thread, and contains additional interface
-  // information such as interface types (i.e. wifi vs cellular).
-  net::IPAddressList chrome_os_address_list;
-  InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::Ethernet(),
-                                &chrome_os_address_list);
-  InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::WiFi(),
-                                &chrome_os_address_list);
-  DiscoverOnAddresses(chrome_os_address_list);
-
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::UI, FROM_HERE, base::Bind(&GetBestBindAddressOnUIThread),
+      base::Bind(&DialServiceImpl::DiscoverOnAddresses,
+                 weak_factory_.GetWeakPtr()));
 #else
   BrowserThread::PostTaskAndReplyWithResult(
       BrowserThread::FILE, FROM_HERE, base::Bind(&GetNetworkListOnFileThread),
diff --git a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
index 54e2826..861af64 100644
--- a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
@@ -87,7 +87,7 @@
          it != values->end(); ++it) {
       const base::DictionaryValue* dict;
       MediaDeviceInfo device;
-      ASSERT_TRUE((*it)->GetAsDictionary(&dict));
+      ASSERT_TRUE(it->GetAsDictionary(&dict));
       ASSERT_TRUE(dict->GetString("deviceId", &device.device_id));
       ASSERT_TRUE(dict->GetString("kind", &device.kind));
       ASSERT_TRUE(dict->GetString("label", &device.label));
diff --git a/chrome/browser/media_galleries/media_galleries_preferences.cc b/chrome/browser/media_galleries/media_galleries_preferences.cc
index db859f5..649e310 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences.cc
@@ -621,7 +621,7 @@
        iter != list->end(); ++iter) {
     // All of these calls should succeed, but preferences file can be corrupt.
     base::DictionaryValue* dict;
-    if (!(*iter)->GetAsDictionary(&dict))
+    if (!iter->GetAsDictionary(&dict))
       continue;
     std::string this_device_id;
     if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
@@ -713,7 +713,7 @@
     for (base::ListValue::const_iterator it = list->begin();
          it != list->end(); ++it) {
       const base::DictionaryValue* dict = NULL;
-      if (!(*it)->GetAsDictionary(&dict))
+      if (!it->GetAsDictionary(&dict))
         continue;
 
       MediaGalleryPrefInfo gallery_info;
@@ -957,13 +957,11 @@
         new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
     base::ListValue* list = update->Get();
 
-    for (base::ListValue::const_iterator list_iter = list->begin();
-         list_iter != list->end();
-         ++list_iter) {
+    for (base::ListValue::iterator list_iter = list->begin();
+         list_iter != list->end(); ++list_iter) {
       base::DictionaryValue* dict;
       MediaGalleryPrefId iter_id;
-      if ((*list_iter)->GetAsDictionary(&dict) &&
-          GetPrefId(*dict, &iter_id) &&
+      if (list_iter->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
           *pref_id_it == iter_id) {
         if (update_gallery_type)
           dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(new_type));
@@ -1058,7 +1056,7 @@
     base::DictionaryValue* dict;
     MediaGalleryPrefId pref_id;
 
-    if (!((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &pref_id)))
+    if (!(iter->GetAsDictionary(&dict) && GetPrefId(*dict, &pref_id)))
       continue;
 
     std::string default_gallery_type_string;
@@ -1155,7 +1153,7 @@
        iter != list->end(); ++iter) {
     base::DictionaryValue* dict;
     MediaGalleryPrefId iter_id;
-    if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
+    if (iter->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
         id == iter_id) {
       RemoveGalleryPermissionsFromPrefs(id);
       MediaGalleryPrefInfo::Type type;
@@ -1333,7 +1331,7 @@
     for (base::ListValue::iterator iter = permissions->begin();
          iter != permissions->end(); ++iter) {
       base::DictionaryValue* dict = NULL;
-      if (!(*iter)->GetAsDictionary(&dict))
+      if (!iter->GetAsDictionary(&dict))
         continue;
       MediaGalleryPermission perm;
       if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
@@ -1370,7 +1368,7 @@
   for (base::ListValue::iterator iter = permissions->begin();
        iter != permissions->end(); ++iter) {
     const base::DictionaryValue* dict = NULL;
-    if (!(*iter)->GetAsDictionary(&dict))
+    if (!iter->GetAsDictionary(&dict))
       continue;
     MediaGalleryPermission perm;
     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
@@ -1397,8 +1395,8 @@
 
   for (base::ListValue::const_iterator iter = permissions->begin();
        iter != permissions->end(); ++iter) {
-    base::DictionaryValue* dict = NULL;
-    if (!(*iter)->GetAsDictionary(&dict))
+    const base::DictionaryValue* dict = NULL;
+    if (!iter->GetAsDictionary(&dict))
       continue;
     MediaGalleryPermission perm;
     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
diff --git a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
index 7e966ca..f292e1c 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
@@ -191,7 +191,7 @@
          ++iter) {
       base::DictionaryValue* dict;
 
-      if ((*iter)->GetAsDictionary(&dict)) {
+      if (iter->GetAsDictionary(&dict)) {
         // Setting the prefs version to 2 which is the version before
         // default_gallery_type was added.
         dict->SetInteger(kMediaGalleriesPrefsVersionKey, 2);
diff --git a/chrome/browser/metrics/plugin_metrics_provider.cc b/chrome/browser/metrics/plugin_metrics_provider.cc
index ebd235a5..aaa2f5f 100644
--- a/chrome/browser/metrics/plugin_metrics_provider.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider.cc
@@ -144,8 +144,8 @@
   metrics::SystemProfileProto::Stability* stability =
       system_profile_proto->mutable_stability();
   for (const auto& value : *plugin_stats_list) {
-    base::DictionaryValue* plugin_dict;
-    if (!value->GetAsDictionary(&plugin_dict)) {
+    const base::DictionaryValue* plugin_dict;
+    if (!value.GetAsDictionary(&plugin_dict)) {
       NOTREACHED();
       continue;
     }
@@ -210,9 +210,9 @@
   base::ListValue* plugins = update.Get();
   DCHECK(plugins);
 
-  for (const auto& value : *plugins) {
+  for (auto& value : *plugins) {
     base::DictionaryValue* plugin_dict;
-    if (!value->GetAsDictionary(&plugin_dict)) {
+    if (!value.GetAsDictionary(&plugin_dict)) {
       NOTREACHED();
       continue;
     }
diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc
index 72266b3..04a7ec4 100644
--- a/chrome/browser/net/predictor.cc
+++ b/chrome/browser/net/predictor.cc
@@ -269,13 +269,12 @@
   if (startup_list) {
     base::ListValue::const_iterator it = startup_list->begin();
     int format_version = -1;
-    if (it != startup_list->end() &&
-        (*it)->GetAsInteger(&format_version) &&
+    if (it != startup_list->end() && it->GetAsInteger(&format_version) &&
         format_version == kPredictorStartupFormatVersion) {
       ++it;
       for (; it != startup_list->end(); ++it) {
         std::string url_spec;
-        if (!(*it)->GetAsString(&url_spec)) {
+        if (!it->GetAsString(&url_spec)) {
           LOG(DFATAL);
           break;  // Format incompatibility.
         }
diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc
index 9b947c0b..04690c7 100644
--- a/chrome/browser/net/predictor_unittest.cc
+++ b/chrome/browser/net/predictor_unittest.cc
@@ -106,6 +106,8 @@
 
     // ...and make it part of the serialized referral_list.
     referral_list->Append(base::WrapUnique(motivation_list));
+    // |motivation_list| is invalidated at this point, so it needs to be reset.
+    referral_list->GetList(referral_list->GetSize() - 1, &motivation_list);
   }
 
   base::ListValue* subresource_list(NULL);
diff --git a/chrome/browser/notifications/native_notification_display_service.cc b/chrome/browser/notifications/native_notification_display_service.cc
index 169f175e..b9aa26d6 100644
--- a/chrome/browser/notifications/native_notification_display_service.cc
+++ b/chrome/browser/notifications/native_notification_display_service.cc
@@ -15,6 +15,11 @@
 #include "chrome/browser/notifications/persistent_notification_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/features/features.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/api/notifications/extension_notification_handler.h"
+#endif
 
 namespace {
 
@@ -41,6 +46,10 @@
                          base::MakeUnique<NonPersistentNotificationHandler>());
   AddNotificationHandler(NotificationCommon::PERSISTENT,
                          base::MakeUnique<PersistentNotificationHandler>());
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  AddNotificationHandler(NotificationCommon::EXTENSION,
+                         base::MakeUnique<ExtensionNotificationHandler>());
+#endif
 }
 
 NativeNotificationDisplayService::~NativeNotificationDisplayService() {}
diff --git a/chrome/browser/notifications/non_persistent_notification_handler.cc b/chrome/browser/notifications/non_persistent_notification_handler.cc
index 6222cd2..e40b79c 100644
--- a/chrome/browser/notifications/non_persistent_notification_handler.cc
+++ b/chrome/browser/notifications/non_persistent_notification_handler.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/notifications/notification_delegate.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 
-NonPersistentNotificationHandler::NonPersistentNotificationHandler() {}
-NonPersistentNotificationHandler::~NonPersistentNotificationHandler() {}
+NonPersistentNotificationHandler::NonPersistentNotificationHandler() = default;
+NonPersistentNotificationHandler::~NonPersistentNotificationHandler() = default;
 
 void NonPersistentNotificationHandler::OnClose(
     Profile* profile,
@@ -28,8 +28,6 @@
     const std::string& notification_id,
     int action_index,
     const base::NullableString16& reply) {
-  // Buttons and replies not supported for non persistent notifications.
-  DCHECK_EQ(action_index, -1);
   DCHECK(reply.is_null());
 
   if (notifications_.find(notification_id) != notifications_.end()) {
diff --git a/chrome/browser/notifications/notification_common.h b/chrome/browser/notifications/notification_common.h
index 9a6e678c..9bf8d48 100644
--- a/chrome/browser/notifications/notification_common.h
+++ b/chrome/browser/notifications/notification_common.h
@@ -13,6 +13,7 @@
 class NotificationCommon {
  public:
   // Things as user can do to a notification.
+  // TODO(peter): Prefix these options with OPERATION_.
   enum Operation {
     CLICK = 0,
     CLOSE = 1,
@@ -21,6 +22,7 @@
   };
 
   // Possible kinds of notifications
+  // TODO(peter): Prefix these options with TYPE_.
   enum Type {
     PERSISTENT = 0,
     NON_PERSISTENT = 1,
diff --git a/chrome/browser/notifications/platform_notification_service_unittest.cc b/chrome/browser/notifications/platform_notification_service_unittest.cc
index ae04889..4812b40 100644
--- a/chrome/browser/notifications/platform_notification_service_unittest.cc
+++ b/chrome/browser/notifications/platform_notification_service_unittest.cc
@@ -98,6 +98,8 @@
     std::unique_ptr<NotificationPlatformBridge> notification_bridge =
         base::MakeUnique<StubNotificationPlatformBridge>();
 
+    // TODO(peter): These tests should use the NotificationDisplayService, which
+    // will allow the StubNotificationPlatformBridge to be removed.
     TestingBrowserProcess::GetGlobal()->SetNotificationUIManager(
         std::move(ui_manager));
     TestingBrowserProcess::GetGlobal()->SetNotificationPlatformBridge(
diff --git a/chrome/browser/notifications/stub_notification_display_service.cc b/chrome/browser/notifications/stub_notification_display_service.cc
new file mode 100644
index 0000000..981d472
--- /dev/null
+++ b/chrome/browser/notifications/stub_notification_display_service.cc
@@ -0,0 +1,89 @@
+// 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/notifications/stub_notification_display_service.h"
+
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+
+// static
+std::unique_ptr<KeyedService> StubNotificationDisplayService::FactoryForTests(
+    content::BrowserContext* /* browser_context */) {
+  return base::MakeUnique<StubNotificationDisplayService>();
+}
+
+StubNotificationDisplayService::StubNotificationDisplayService() = default;
+
+StubNotificationDisplayService::~StubNotificationDisplayService() = default;
+
+void StubNotificationDisplayService::RemoveNotification(
+    NotificationCommon::Type notification_type,
+    const std::string& notification_id,
+    bool by_user) {
+  auto iter = std::find_if(
+      notifications_.begin(), notifications_.end(),
+      [notification_type, notification_id](const NotificationData& data) {
+        return data.first == notification_type &&
+               data.second.delegate_id() == notification_id;
+      });
+
+  if (iter == notifications_.end())
+    return;
+
+  // TODO(peter): Invoke the handlers when that has been generalized.
+  iter->second.delegate()->Close(by_user);
+
+  notifications_.erase(iter);
+}
+
+void StubNotificationDisplayService::RemoveAllNotifications(
+    NotificationCommon::Type notification_type,
+    bool by_user) {
+  for (auto iter = notifications_.begin(); iter != notifications_.end();) {
+    if (iter->first == notification_type) {
+      // TODO(peter): Invoke the handlers when that has been generalized.
+      iter->second.delegate()->Close(by_user);
+
+      iter = notifications_.erase(iter);
+    } else {
+      iter++;
+    }
+  }
+}
+
+void StubNotificationDisplayService::Display(
+    NotificationCommon::Type notification_type,
+    const std::string& notification_id,
+    const Notification& notification) {
+  // This mimics notification replacement behaviour; the Close() method on a
+  // notification's delegate is not meant to be invoked in this situation.
+  Close(notification_type, notification_id);
+
+  notifications_.emplace_back(notification_type, notification);
+}
+
+void StubNotificationDisplayService::Close(
+    NotificationCommon::Type notification_type,
+    const std::string& notification_id) {
+  notifications_.erase(
+      std::remove_if(
+          notifications_.begin(), notifications_.end(),
+          [notification_type, notification_id](const NotificationData& data) {
+            return data.first == notification_type &&
+                   data.second.delegate_id() == notification_id;
+          }),
+      notifications_.end());
+}
+
+void StubNotificationDisplayService::GetDisplayed(
+    const DisplayedNotificationsCallback& callback) const {
+  std::unique_ptr<std::set<std::string>> notifications =
+      base::MakeUnique<std::set<std::string>>();
+
+  for (const auto& notification_data : notifications_)
+    notifications->insert(notification_data.second.delegate_id());
+
+  callback.Run(std::move(notifications), true /* supports_synchronization */);
+}
diff --git a/chrome/browser/notifications/stub_notification_display_service.h b/chrome/browser/notifications/stub_notification_display_service.h
new file mode 100644
index 0000000..e87800c
--- /dev/null
+++ b/chrome/browser/notifications/stub_notification_display_service.h
@@ -0,0 +1,57 @@
+// 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_NOTIFICATIONS_STUB_NOTIFICATION_DISPLAY_SERVICE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_STUB_NOTIFICATION_DISPLAY_SERVICE_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_common.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+
+// Implementation of the NotificationDisplayService interface that can be used
+// for testing purposes. Supports additional methods enabling instrumenting the
+// faked underlying notification system.
+class StubNotificationDisplayService : public NotificationDisplayService {
+ public:
+  // Factory function to be used with NotificationDisplayServiceFactory's
+  // SetTestingFactory method, overriding the default display service.
+  static std::unique_ptr<KeyedService> FactoryForTests(
+      content::BrowserContext* browser_context);
+
+  StubNotificationDisplayService();
+  ~StubNotificationDisplayService() override;
+
+  // Removes the notification identified by |notification_id|.
+  void RemoveNotification(NotificationCommon::Type notification_type,
+                          const std::string& notification_id,
+                          bool by_user);
+
+  // Removes all notifications shown by this display service.
+  void RemoveAllNotifications(NotificationCommon::Type notification_type,
+                              bool by_user);
+
+  // NotificationDisplayService implementation:
+  void Display(NotificationCommon::Type notification_type,
+               const std::string& notification_id,
+               const Notification& notification) override;
+  void Close(NotificationCommon::Type notification_type,
+             const std::string& notification_id) override;
+  void GetDisplayed(
+      const DisplayedNotificationsCallback& callback) const override;
+
+ private:
+  // Data to store for a notification that's being shown through this service.
+  using NotificationData = std::pair<NotificationCommon::Type, Notification>;
+
+  std::vector<NotificationData> notifications_;
+
+  DISALLOW_COPY_AND_ASSIGN(StubNotificationDisplayService);
+};
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_STUB_NOTIFICATION_DISPLAY_SERVICE_H_
diff --git a/chrome/browser/permissions/chooser_context_base.cc b/chrome/browser/permissions/chooser_context_base.cc
index 3b99bad..1214898c8 100644
--- a/chrome/browser/permissions/chooser_context_base.cc
+++ b/chrome/browser/permissions/chooser_context_base.cc
@@ -58,7 +58,8 @@
   for (auto& object : *object_list) {
     // Steal ownership of |object| from |object_list|.
     std::unique_ptr<base::DictionaryValue> object_dict =
-        base::DictionaryValue::From(std::move(object));
+        base::DictionaryValue::From(
+            base::MakeUnique<base::Value>(std::move(object)));
     if (object_dict && IsValidObject(*object_dict))
       results.push_back(std::move(object_dict));
   }
@@ -84,9 +85,9 @@
     if (!setting->GetList(kObjectListKey, &object_list))
       continue;
 
-    for (const auto& object : *object_list) {
+    for (auto& object : *object_list) {
       base::DictionaryValue* object_dict;
-      if (!object->GetAsDictionary(&object_dict) ||
+      if (!object.GetAsDictionary(&object_dict) ||
           !IsValidObject(*object_dict)) {
         continue;
       }
diff --git a/chrome/browser/plugins/plugin_finder.cc b/chrome/browser/plugins/plugin_finder.cc
index 5ac803d..75c6ecab 100644
--- a/chrome/browser/plugins/plugin_finder.cc
+++ b/chrome/browser/plugins/plugin_finder.cc
@@ -80,7 +80,7 @@
   for (base::ListValue::const_iterator mime_type_it = mime_types->begin();
        mime_type_it != mime_types->end(); ++mime_type_it) {
     std::string mime_type_str;
-    success = (*mime_type_it)->GetAsString(&mime_type_str);
+    success = mime_type_it->GetAsString(&mime_type_str);
     DCHECK(success);
     if (matching_mime_types) {
       plugin->AddMatchingMimeType(mime_type_str);
@@ -115,8 +115,8 @@
   if (plugin_dict->GetList("versions", &versions)) {
     for (base::ListValue::const_iterator it = versions->begin();
          it != versions->end(); ++it) {
-      base::DictionaryValue* version_dict = NULL;
-      if (!(*it)->GetAsDictionary(&version_dict)) {
+      const base::DictionaryValue* version_dict = NULL;
+      if (!it->GetAsDictionary(&version_dict)) {
         NOTREACHED();
         continue;
       }
diff --git a/chrome/browser/plugins/plugin_finder_unittest.cc b/chrome/browser/plugins/plugin_finder_unittest.cc
index 4cb60f83..d906f06 100644
--- a/chrome/browser/plugins/plugin_finder_unittest.cc
+++ b/chrome/browser/plugins/plugin_finder_unittest.cc
@@ -43,7 +43,7 @@
     if (plugin->GetList("mime_types", &mime_types)) {
       for (base::ListValue::const_iterator mime_type_it = mime_types->begin();
            mime_type_it != mime_types->end(); ++mime_type_it) {
-        EXPECT_TRUE((*mime_type_it)->GetAsString(&dummy_str));
+        EXPECT_TRUE(mime_type_it->GetAsString(&dummy_str));
       }
     }
 
@@ -51,7 +51,7 @@
     if (plugin->GetList("matching_mime_types", &matching_mime_types)) {
       for (base::ListValue::const_iterator it = matching_mime_types->begin();
            it != matching_mime_types->end(); ++it) {
-        EXPECT_TRUE((*it)->GetAsString(&dummy_str));
+        EXPECT_TRUE(it->GetAsString(&dummy_str));
       }
     }
 
@@ -61,8 +61,8 @@
 
     for (base::ListValue::const_iterator it = versions->begin();
          it != versions->end(); ++it) {
-      base::DictionaryValue* version_dict = NULL;
-      ASSERT_TRUE((*it)->GetAsDictionary(&version_dict));
+      const base::DictionaryValue* version_dict = NULL;
+      ASSERT_TRUE(it->GetAsDictionary(&version_dict));
       EXPECT_TRUE(version_dict->GetString("version", &dummy_str));
       std::string status_str;
       EXPECT_TRUE(version_dict->GetString("status", &status_str));
diff --git a/chrome/browser/plugins/plugin_prefs.cc b/chrome/browser/plugins/plugin_prefs.cc
index 3f8c20c0..0c418d1cd 100644
--- a/chrome/browser/plugins/plugin_prefs.cc
+++ b/chrome/browser/plugins/plugin_prefs.cc
@@ -129,9 +129,9 @@
     ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
     base::ListValue* saved_plugins_list = update.Get();
     if (saved_plugins_list && !saved_plugins_list->empty()) {
-      for (const auto& plugin_value : *saved_plugins_list) {
+      for (auto& plugin_value : *saved_plugins_list) {
         base::DictionaryValue* plugin;
-        if (!plugin_value->GetAsDictionary(&plugin)) {
+        if (!plugin_value.GetAsDictionary(&plugin)) {
           LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
           continue;  // Oops, don't know what to do with this item.
         }
diff --git a/chrome/browser/policy/managed_bookmarks_policy_handler.cc b/chrome/browser/policy/managed_bookmarks_policy_handler.cc
index dd5fa8b..b112a0c 100644
--- a/chrome/browser/policy/managed_bookmarks_policy_handler.cc
+++ b/chrome/browser/policy/managed_bookmarks_policy_handler.cc
@@ -51,7 +51,8 @@
   // Iterate over the list, and try to find the FolderName.
   for (const auto& el : list) {
     const base::DictionaryValue* dict = NULL;
-    if (!el || !el->GetAsDictionary(&dict)) continue;
+    if (!el.GetAsDictionary(&dict))
+      continue;
 
     std::string name;
     if (dict->GetString(ManagedBookmarksTracker::kFolderName, &name)) {
@@ -68,7 +69,7 @@
   base::ListValue::iterator it = list->begin();
   while (it != list->end()) {
     base::DictionaryValue* dict = NULL;
-    if (!*it || !(*it)->GetAsDictionary(&dict)) {
+    if (!it->GetAsDictionary(&dict)) {
       it = list->Erase(it, NULL);
       continue;
     }
diff --git a/chrome/browser/policy/policy_prefs_browsertest.cc b/chrome/browser/policy/policy_prefs_browsertest.cc
index 89b5b7ae..7517b24 100644
--- a/chrome/browser/policy/policy_prefs_browsertest.cc
+++ b/chrome/browser/policy/policy_prefs_browsertest.cc
@@ -452,7 +452,7 @@
   for (base::ListValue::const_iterator indicator = indicators->begin();
        indicator != indicators->end(); ++indicator) {
     const base::DictionaryValue* properties = NULL;
-    ASSERT_TRUE((*indicator)->GetAsDictionary(&properties));
+    ASSERT_TRUE(indicator->GetAsDictionary(&properties));
     std::string indicator_value;
     std::string indicator_controlled_by;
     bool indicator_readonly;
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
index 5244771..f52d8aa 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
@@ -57,7 +57,7 @@
     std::string cipher_string;
     for (base::ListValue::const_iterator it = list_value->begin();
          it != list_value->end(); ++it, ++ciphers) {
-      ASSERT_TRUE((*it)->GetAsString(&cipher_string));
+      ASSERT_TRUE(it->GetAsString(&cipher_string));
       EXPECT_EQ(*ciphers, cipher_string);
     }
   }
diff --git a/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc b/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
index d826da1..3e12b0e 100644
--- a/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
+++ b/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
@@ -32,8 +32,8 @@
 
   DeviceList devices;
   for (const auto& printer : *printers) {
-    base::DictionaryValue* printer_dict;
-    if (!printer->GetAsDictionary(&printer_dict))
+    const base::DictionaryValue* printer_dict;
+    if (!printer.GetAsDictionary(&printer_dict))
       continue;
 
     Device printer_details;
diff --git a/chrome/browser/printing/cloud_print/gcd_api_flow_impl.cc b/chrome/browser/printing/cloud_print/gcd_api_flow_impl.cc
index 0f389481..16a1962 100644
--- a/chrome/browser/printing/cloud_print/gcd_api_flow_impl.cc
+++ b/chrome/browser/printing/cloud_print/gcd_api_flow_impl.cc
@@ -39,7 +39,7 @@
           sender: "Cloud Print"
           description:
             "Registers a locally discovered Privet printer with a Cloud Print "
-            "Server.
+            "Server."
           trigger:
             "Users can select Privet printers on chrome://devices/ and "
             "register them."
@@ -61,7 +61,7 @@
           description:
             "Queries a Cloud Print Server for the list of printers."
           trigger:
-            "chrome://devices/ fetches the list when the user logs in,
+            "chrome://devices/ fetches the list when the user logs in, "
             "re-enable the Cloud Print service, or manually requests a printer "
             "list refresh."
           data: "None"
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index 018ce88d..88de55f3 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -825,7 +825,7 @@
   for (base::ListValue::iterator i = startup_list->begin();
        i != startup_list->end(); ++i) {
     std::string url;
-    EXPECT_TRUE((*i)->GetAsString(&url));
+    EXPECT_TRUE(i->GetAsString(&url));
     startup_pages.push_back(url);
   }
   ASSERT_EQ(2u, startup_pages.size());
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 93f1a8b..26308bf 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -680,8 +680,7 @@
     base::ListValue::const_iterator it;
     for (it = profile_list->begin(); it != profile_list->end(); ++it) {
       std::string profile_path;
-      if (!(*it)->GetAsString(&profile_path) ||
-          profile_path.empty() ||
+      if (!it->GetAsString(&profile_path) || profile_path.empty() ||
           profile_path ==
               base::FilePath(chrome::kSystemProfileDir).AsUTF8Unsafe()) {
         LOG(WARNING) << "Invalid entry in " << prefs::kProfilesLastActive;
@@ -903,10 +902,10 @@
       local_state->GetList(prefs::kProfilesDeleted);
   DCHECK(deleted_profiles);
 
-  for (const std::unique_ptr<base::Value>& value : *deleted_profiles) {
+  for (const base::Value& value : *deleted_profiles) {
     base::FilePath profile_path;
     bool is_valid_profile_path =
-        base::GetValueAsFilePath(*value, &profile_path) &&
+        base::GetValueAsFilePath(value, &profile_path) &&
         profile_path.DirName() == user_data_dir();
     // Although it should never happen, make sure this is a valid path in the
     // user_data_dir, so we don't accidentially delete something else.
@@ -917,12 +916,11 @@
         BrowserThread::PostTaskAndReply(
             BrowserThread::FILE, FROM_HERE,
             base::Bind(&NukeProfileFromDisk, profile_path),
-            base::Bind(&ProfileCleanedUp, value.get()));
+            base::Bind(&ProfileCleanedUp, &value));
       } else {
         // Everything is fine, the profile was removed on shutdown.
-        BrowserThread::PostTask(
-          BrowserThread::UI, FROM_HERE,
-          base::Bind(&ProfileCleanedUp, value.get()));
+        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                                base::Bind(&ProfileCleanedUp, &value));
       }
     } else {
       LOG(ERROR) << "Found invalid profile path in deleted_profiles: "
diff --git a/chrome/browser/resources/chromeos/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/switch_access/BUILD.gn
index 7ac565b0..9c11288 100644
--- a/chrome/browser/resources/chromeos/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/switch_access/BUILD.gn
@@ -41,6 +41,26 @@
   ]
 }
 
+test("switch_access_tests") {
+  deps = [
+    ":switch_access_webuijs_tests",
+    "//chrome/test:browser_tests_runner",
+  ]
+}
+
+js2gtest("switch_access_webuijs_tests") {
+  test_type = "webui"
+  sources = [
+    "auto_scan_manager_unittest.gtestjs",
+    "tree_walker_unittest.gtestjs",
+  ]
+  extra_js_files = [
+    "auto_scan_manager.js",
+    "tree_walker.js",
+  ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+}
+
 # TODO: refactor this into another file like generate_manifest.gni
 # to share with other extensions.
 template("manifest") {
diff --git a/chrome/browser/resources/chromeos/switch_access/auto_scan_manager.js b/chrome/browser/resources/chromeos/switch_access/auto_scan_manager.js
index f709547..24043ae1 100644
--- a/chrome/browser/resources/chromeos/switch_access/auto_scan_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/auto_scan_manager.js
@@ -67,11 +67,13 @@
   },
 
   /**
-   * Restart auto-scan under the current settings.
+   * Restart auto-scan under the current settings if it is currently running.
    */
-  restart: function() {
-    this.stop_();
-    this.start_();
+  restartIfRunning: function() {
+    if (this.isRunning()) {
+      this.stop_();
+      this.start_();
+    }
   },
 
   /**
@@ -95,8 +97,6 @@
    */
   setScanTime: function(scanTime) {
     this.scanTime_ = scanTime;
-    if (this.isRunning()) {
-      this.restart();
-    }
+    this.restartIfRunning();
   },
 };
diff --git a/chrome/browser/resources/chromeos/switch_access/auto_scan_manager_unittest.gtestjs b/chrome/browser/resources/chromeos/switch_access/auto_scan_manager_unittest.gtestjs
new file mode 100644
index 0000000..d2b79fa
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/auto_scan_manager_unittest.gtestjs
@@ -0,0 +1,176 @@
+// 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.
+
+/**
+ * @constructor
+ * @implements {SwitchAccessInterface}
+ */
+function FakeSwitchAccess() {
+  this.moveToNextCount = 0;
+  this.switchAccessPrefs = {};
+  this.switchAccessPrefs.getNumberPref = function(key) {
+    if (key === 'autoScanTime')
+      return 1;
+  };
+  this.switchAccessPrefs.getBooleanPref = function(key) {
+    if (key === 'enableAutoScan')
+      return false;
+  };
+};
+
+FakeSwitchAccess.prototype = {
+  /** @override */
+  moveToNode: function(doNext) {
+    if (doNext)
+      this.moveToNextCount += 1;
+  },
+};
+
+/**
+ * Test fixture for auto_scan_manager.js.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function AutoScanManagerUnitTest() {
+  testing.Test.call(this);
+};
+
+AutoScanManagerUnitTest.prototype = {
+  __proto__: testing.Test.prototype,
+
+  /** @override */
+  extraLibraries: [
+    'auto_scan_manager.js',
+  ],
+
+  /** @override */
+  isAsync: true,
+
+  /** @override */
+  browsePreload: DUMMY_URL,
+
+  /** @override */
+  setUp: function() {
+    // Use intervalCount and intervalDelay to check how many intervals are
+    // currently running (should be 1 at most) and what the current delay is.
+    window.intervalCount = 0;
+    window.intervalDelay = undefined;
+    window.realSetInterval = window.setInterval;
+    window.realClearInterval = window.clearInterval;
+
+    window.setInterval = function(func, delay) {
+      window.intervalCount += 1;
+      window.intervalDelay = delay;
+
+      // Set the actual delay to 1 ms to speed up the test.
+      return window.realSetInterval(func, 1);
+    };
+
+    window.clearInterval = function(intervalID) {
+      if (intervalID)
+        window.intervalCount -= 1;
+      window.realClearInterval(intervalID);
+    };
+  }
+};
+
+TEST_F('AutoScanManagerUnitTest', 'SetEnabled', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+  assertEquals(0, switchAccess.moveToNextCount);
+  assertEquals(0, window.intervalCount);
+
+  autoScanManager.setEnabled(true);
+  window.setTimeout(function() {
+    assertTrue(autoScanManager.isRunning());
+    assertGT(switchAccess.moveToNextCount, 1);
+    assertEquals(1, window.intervalCount);
+    testDone();
+  }, 10);
+});
+
+TEST_F('AutoScanManagerUnitTest', 'EnableMultiple', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+  assertEquals(0, switchAccess.moveToNextCount);
+  assertEquals(0, window.intervalCount);
+
+  autoScanManager.setEnabled(true);
+  autoScanManager.setEnabled(true);
+  autoScanManager.setEnabled(true);
+  window.setTimeout(function() {
+    assertTrue(autoScanManager.isRunning());
+    assertGT(switchAccess.moveToNextCount, 1);
+    assertEquals(1, window.intervalCount);
+    testDone();
+  }, 10);
+});
+
+TEST_F('AutoScanManagerUnitTest', 'EnableAndDisable', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+
+  autoScanManager.setEnabled(true);
+  assertTrue(autoScanManager.isRunning());
+
+  autoScanManager.setEnabled(false);
+  assertFalse(autoScanManager.isRunning());
+  testDone();
+});
+
+TEST_F('AutoScanManagerUnitTest', 'RestartMultiple', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+  assertEquals(0, switchAccess.moveToNextCount);
+  assertEquals(0, window.intervalCount);
+
+  autoScanManager.setEnabled(true);
+  autoScanManager.restartIfRunning();
+  autoScanManager.restartIfRunning();
+  autoScanManager.restartIfRunning();
+  window.setTimeout(function() {
+    assertTrue(autoScanManager.isRunning());
+    assertGT(switchAccess.moveToNextCount, 1);
+    assertEquals(1, window.intervalCount);
+    testDone();
+  }, 10);
+});
+
+TEST_F('AutoScanManagerUnitTest', 'RestartWhenOff', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+
+  autoScanManager.restartIfRunning();
+  assertFalse(autoScanManager.isRunning());
+  testDone();
+});
+
+TEST_F('AutoScanManagerUnitTest', 'SetScanTime', function() {
+  let switchAccess = new FakeSwitchAccess();
+  let autoScanManager = new AutoScanManager(switchAccess);
+  assertFalse(autoScanManager.isRunning());
+  assertEquals(1, autoScanManager.scanTime_);
+  assertEquals(undefined, window.intervalDelay);
+
+  autoScanManager.setScanTime(2);
+  assertFalse(autoScanManager.isRunning());
+  assertEquals(2, autoScanManager.scanTime_);
+  assertEquals(undefined, window.intervalDelay);
+
+  autoScanManager.setEnabled(true);
+  assertTrue(autoScanManager.isRunning());
+  assertEquals(2, autoScanManager.scanTime_);
+  assertEquals(2, window.intervalDelay);
+
+  autoScanManager.setScanTime(5);
+  assertTrue(autoScanManager.isRunning());
+  assertEquals(5, autoScanManager.scanTime_);
+  assertEquals(5, window.intervalDelay);
+  testDone();
+});
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access.js b/chrome/browser/resources/chromeos/switch_access/switch_access.js
index 57ad0f32..0adc858 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access.js
@@ -135,8 +135,7 @@
    * @override
    */
   performedUserAction: function() {
-    if (this.autoScanManager_.isRunning())
-      this.autoScanManager_.restart();
+    this.autoScanManager_.restartIfRunning();
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/switch_access/tree_walker_unittest.gtestjs b/chrome/browser/resources/chromeos/switch_access/tree_walker_unittest.gtestjs
index ff1d046..52782ee 100644
--- a/chrome/browser/resources/chromeos/switch_access/tree_walker_unittest.gtestjs
+++ b/chrome/browser/resources/chromeos/switch_access/tree_walker_unittest.gtestjs
@@ -19,6 +19,9 @@
     'tree_walker.js',
   ],
 
+  /** @override */
+  browsePreload: DUMMY_URL,
+
   getSampleTree: function() {
     // root
     //   middle1
@@ -75,14 +78,45 @@
   }
 };
 
+TEST_F('AutomationTreeWalkerUnitTest', 'MoveToNode', function() {
+  let t = this.getSampleTree();
+  let treeWalker = new AutomationTreeWalker();
+
+  let interesting = {focusable: true};
+  t.leaf1.state = interesting;
+  t.leaf2.state = interesting;
+  t.middle2.state = interesting;
+  t.leaf5.state = interesting;
+
+  // Move to next node.
+  assertEquals(t.leaf1, treeWalker.moveToNode(t.root, t.root, true));
+  assertEquals(t.leaf1, treeWalker.moveToNode(t.middle1, t.root, true));
+  assertEquals(t.leaf2, treeWalker.moveToNode(t.leaf1, t.root, true));
+  assertEquals(t.middle2, treeWalker.moveToNode(t.leaf2, t.root, true));
+  assertEquals(t.middle2, treeWalker.moveToNode(t.leaf3, t.root, true));
+  assertEquals(t.leaf5, treeWalker.moveToNode(t.middle2, t.root, true));
+  assertEquals(t.leaf5, treeWalker.moveToNode(t.leaf4, t.root, true));
+  assertEquals(t.leaf1, treeWalker.moveToNode(t.leaf5, t.root, true));
+
+  // Move to previous node.
+  assertEquals(t.middle2, treeWalker.moveToNode(t.leaf5, t.root, false));
+  assertEquals(t.middle2, treeWalker.moveToNode(t.leaf4, t.root, false));
+  assertEquals(t.leaf2, treeWalker.moveToNode(t.middle2, t.root, false));
+  assertEquals(t.leaf2, treeWalker.moveToNode(t.leaf3, t.root, false));
+  assertEquals(t.leaf1, treeWalker.moveToNode(t.leaf2, t.root, false));
+  assertEquals(t.leaf5, treeWalker.moveToNode(t.leaf1, t.root, false));
+  assertEquals(t.leaf5, treeWalker.moveToNode(t.middle1, t.root, false));
+  assertEquals(t.leaf5, treeWalker.moveToNode(t.root, t.root, false));
+});
+
 TEST_F('AutomationTreeWalkerUnitTest', 'GetNextNode', function() {
-  let tree = this.getSampleTree();
+  let t = this.getSampleTree();
   let treeWalker = new AutomationTreeWalker();
 
   let order =
-      [tree.root, tree.middle1, tree.leaf1, tree.leaf2, tree.leaf3,
-      tree.middle2, tree.leaf4, tree.leaf5];
-  let node = tree.root;
+      [t.root, t.middle1, t.leaf1, t.leaf2, t.leaf3,
+      t.middle2, t.leaf4, t.leaf5];
+  let node = t.root;
   for (let i = 0; i < order.length; i++) {
     assertEquals(order[i], node);
     node = treeWalker.getNextNode_(node);
@@ -91,13 +125,13 @@
 });
 
 TEST_F('AutomationTreeWalkerUnitTest', 'GetPreviousNode', function() {
-  let tree = this.getSampleTree();
+  let t = this.getSampleTree();
   let treeWalker = new AutomationTreeWalker();
 
   let order =
-      [tree.leaf5, tree.leaf4, tree.middle2, tree.leaf3, tree.leaf2,
-      tree.leaf1, tree.middle1, tree.root];
-  let node = tree.leaf5;
+      [t.leaf5, t.leaf4, t.middle2, t.leaf3, t.leaf2,
+      t.leaf1, t.middle1, t.root];
+  let node = t.leaf5;
   for (let i = 0; i < order.length; i++) {
     assertEquals(order[i], node);
     node = treeWalker.getPreviousNode_(node);
@@ -106,17 +140,17 @@
 });
 
 TEST_F('AutomationTreeWalkerUnitTest', 'GetYoungestDescendant', function() {
-  let tree = this.getSampleTree();
+  let t = this.getSampleTree();
   let treeWalker = new AutomationTreeWalker();
 
-  assertEquals(tree.leaf5, treeWalker.getYoungestDescendant_(tree.root));
-  assertEquals(tree.leaf3, treeWalker.getYoungestDescendant_(tree.middle1));
-  assertEquals(tree.leaf5, treeWalker.getYoungestDescendant_(tree.middle2));
-  assertEquals(undefined, treeWalker.getYoungestDescendant_(tree.leaf1));
-  assertEquals(undefined, treeWalker.getYoungestDescendant_(tree.leaf2));
-  assertEquals(undefined, treeWalker.getYoungestDescendant_(tree.leaf3));
-  assertEquals(undefined, treeWalker.getYoungestDescendant_(tree.leaf4));
-  assertEquals(undefined, treeWalker.getYoungestDescendant_(tree.leaf5));
+  assertEquals(t.leaf5, treeWalker.getYoungestDescendant_(t.root));
+  assertEquals(t.leaf3, treeWalker.getYoungestDescendant_(t.middle1));
+  assertEquals(t.leaf5, treeWalker.getYoungestDescendant_(t.middle2));
+  assertEquals(undefined, treeWalker.getYoungestDescendant_(t.leaf1));
+  assertEquals(undefined, treeWalker.getYoungestDescendant_(t.leaf2));
+  assertEquals(undefined, treeWalker.getYoungestDescendant_(t.leaf3));
+  assertEquals(undefined, treeWalker.getYoungestDescendant_(t.leaf4));
+  assertEquals(undefined, treeWalker.getYoungestDescendant_(t.leaf5));
 });
 
 TEST_F('AutomationTreeWalkerUnitTest', 'IsInteresting', function() {
diff --git a/chrome/browser/resources/sandbox_internals/sandbox_internals.html b/chrome/browser/resources/sandbox_internals/sandbox_internals.html
new file mode 100644
index 0000000..308e08c7
--- /dev/null
+++ b/chrome/browser/resources/sandbox_internals/sandbox_internals.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Sandbox Status</title>
+    <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+    <style type="text/css">
+      #sandbox-status {
+        border: 1px solid gray;
+      }
+      #sandbox-status td {
+        border: 1px solid gray;
+        padding: 3px;
+      }
+      #evaluation {
+        font-weight: bold;
+      }
+      .good {
+        background-color: rgb(143, 218, 146);
+      }
+      .bad {
+        background-color: rgb(249, 156, 149);
+      }
+      .info {
+        background-color: rgb(169, 217, 239);
+      }
+    </style>
+    <script src="chrome://resources/js/cr.js"></script>
+    <if expr="not is_android">
+    <script src="chrome://resources/js/load_time_data.js"></script>
+    <script src="chrome://sandbox/strings.js"></script>
+    </if>
+    <script src="chrome://resources/js/util.js"></script>
+    <script src="sandbox_internals.js"></script>
+  </head>
+  <body>
+    <h1>Sandbox Status</h1>
+
+    <table id="sandbox-status">
+    </table>
+
+    <p id="evaluation"></p>
+  </body>
+</html>
diff --git a/chrome/browser/resources/sandbox_internals/sandbox_internals.js b/chrome/browser/resources/sandbox_internals/sandbox_internals.js
new file mode 100644
index 0000000..02479b5
--- /dev/null
+++ b/chrome/browser/resources/sandbox_internals/sandbox_internals.js
@@ -0,0 +1,139 @@
+// 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.
+
+(function() {
+  let GOOD = 'good';
+  let BAD = 'bad';
+  let INFO = 'info';
+
+  /**
+   * Adds a row to the sandbox status table.
+   * @param {string} name The name of the status item.
+   * @param {string} value The status of the item.
+   * @param {string?} cssClass A CSS class to apply to the row.
+   * @return {Element} The newly added TR.
+   */
+  function addStatusRow(name, value, cssClass) {
+    let row = cr.doc.createElement('tr');
+
+    let nameCol = row.appendChild(cr.doc.createElement('td'));
+    let valueCol = row.appendChild(cr.doc.createElement('td'));
+
+    nameCol.textContent = name;
+    valueCol.textContent = value;
+
+    if (cssClass != null) {
+      nameCol.classList.add(cssClass);
+      valueCol.classList.add(cssClass);
+    }
+
+    $('sandbox-status').appendChild(row);
+    return row;
+  }
+
+  /**
+   * Adds a status row that reports either Yes or No.
+   * @param {string} name The name of the status item.
+   * @param {boolean} result The status (good/bad) result.
+   * @return {Element} The newly added TR.
+   */
+  function addGoodBadRow(name, result) {
+    return addStatusRow(name, result ? 'Yes' : 'No', result ? GOOD : BAD);
+  }
+
+  /**
+   * Reports the overall sandbox status evaluation message.
+   * @param {boolean}
+   */
+  function setEvaluation(result) {
+    let message = result ? 'You are adequately sandboxed.'
+                         : 'You are NOT adequately sandboxed.';
+    $('evaluation').innerText = message;
+  }
+
+  /**
+   * Main page handler for Android.
+   */
+  function androidHandler() {
+    chrome.getAndroidSandboxStatus((status) => {
+      var isIsolated = false;
+      var isTsync = false;
+      var isChromeSeccomp = false;
+
+      addStatusRow('PID', status.pid, INFO);
+      addStatusRow('UID', status.uid, INFO);
+      isIsolated = status.secontext.indexOf(':isolated_app:') != -1;
+      addStatusRow('SELinux Context', status.secontext,
+          isIsolated ? GOOD : BAD);
+
+      let procStatus = status.procStatus.split('\n');
+      for (let line of procStatus) {
+        if (line.startsWith('Seccomp')) {
+          var value = line.split(':')[1].trim();
+          var cssClass = BAD;
+          if (value == '2') {
+            value = 'Yes - TSYNC (' + line + ')';
+            cssClass = GOOD;
+            isTsync = true;
+          } else if (value == '1') {
+            value = 'Yes (' + line + ')';
+          } else {
+            value = line;
+          }
+          addStatusRow('Seccomp-BPF Enabled (Kernel)', value, cssClass);
+          break;
+        }
+      }
+
+      var seccompStatus = 'Unknown';
+      switch (status.seccompStatus) {
+        case 0:
+          seccompStatus = 'Not Supported';
+          break;
+        case 1:
+          seccompStatus = 'Run-time Detection Failed';
+          break;
+        case 2:
+          seccompStatus = 'Disabled by Field Trial';
+          break;
+        case 3:
+          seccompStatus = 'Enabled by Field Trial (not started)';
+          break;
+        case 4:
+          seccompStatus = 'Sandbox Started';
+          isChromeSeccomp = true;
+          break;
+      }
+      addStatusRow('Seccomp-BPF Enabled (Chrome)', seccompStatus,
+          status.seccompStatus == 4 ? GOOD : BAD);
+
+      addStatusRow('Android Build ID', status.androidBuildId, INFO);
+
+      setEvaluation(isIsolated && isTsync && isChromeSeccomp);
+    });
+  }
+
+  /**
+   * Main page handler for desktop Linux.
+   */
+  function linuxHandler() {
+    addGoodBadRow('SUID Sandbox', loadTimeData.getBoolean('suid'));
+    addGoodBadRow('Namespace Sandbox', loadTimeData.getBoolean('userNs'));
+    addGoodBadRow('PID namespaces', loadTimeData.getBoolean('pidNs'));
+    addGoodBadRow('Network namespaces', loadTimeData.getBoolean('netNs'));
+    addGoodBadRow('Seccomp-BPF sandbox', loadTimeData.getBoolean('seccompBpf'));
+    addGoodBadRow('Seccomp-BPF sandbox supports TSYNC',
+        loadTimeData.getBoolean('seccompTsync'));
+    addGoodBadRow('Yama LSM Enforcing', loadTimeData.getBoolean('yama'));
+    setEvaluation(loadTimeData.getBoolean('sandboxGood'));
+  }
+
+  document.addEventListener('DOMContentLoaded', () => {
+    if (cr.isAndroid) {
+      androidHandler();
+    } else {
+      linuxHandler();
+    }
+  });
+})();
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 2913814d..9a26583 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -19,7 +19,7 @@
         section="a11y" focus-config="[[focusConfig_]]">
       <neon-animatable route-path="default">
         <div class="settings-box">
-          <settings-toggle-button id="optionsInMenuToggle"
+          <settings-toggle-button id="optionsInMenuToggle" class="start"
               label="$i18n{optionsInMenuLabel}"
               pref="{{prefs.settings.a11y.enable_menu}}">
           </settings-toggle-button>
diff --git a/chrome/browser/resources/settings/controls/controlled_radio_button.html b/chrome/browser/resources/settings/controls/controlled_radio_button.html
index 6b56e8f..7ef19b4e 100644
--- a/chrome/browser/resources/settings/controls/controlled_radio_button.html
+++ b/chrome/browser/resources/settings/controls/controlled_radio_button.html
@@ -32,7 +32,7 @@
       }
     </style>
 
-    <paper-radio-button id="radioButton" name="{{name}}" checked="{{checked}}"
+    <paper-radio-button id="radioButton" name="{{name}}" checked$="{{checked}}"
         disabled="[[controlled_]]" tabindex$="[[tabindex]]">
       [[label]] <content></content>
     </paper-radio-button>
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
index 7220488..67d72119 100644
--- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
+++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -44,26 +44,26 @@
     </div>
     <div class="settings-box continuation block">
       <h2>$i18n{customDictionaryWords}</h2>
-      <div class="list-frame">
-        <template is="dom-if" if="[[hasWords_(words_.length)]]">
-          <iron-list id="list" items="{{words_}}"
-              scroll-target="[[subpageScrollTarget]]">
-            <template>
-              <div class="list-item">
-                <div class="word text-elide">[[item]]</div>
-                <paper-icon-button icon="cr:clear" on-tap="onRemoveWordTap_"
-                    tabindex$="[[tabIndex]]">
-                </paper-icon-button>
-              </div>
-            </template>
-          </iron-list>
-        </template>
-        <template is="dom-if" if="[[!hasWords_(words_.length)]]">
-          <div id="noWordsLabel" class="list-item">
-            $i18n{noCustomDictionaryWordsFound}
-          </div>
-        </template>
-      </div>
+    </div>
+    <div class="list-frame">
+      <template is="dom-if" if="[[hasWords_(words_.length)]]">
+        <iron-list id="list" items="{{words_}}"
+            scroll-target="[[subpageScrollTarget]]">
+          <template>
+            <div class="list-item">
+              <div class="word text-elide">[[item]]</div>
+              <paper-icon-button icon="cr:clear" on-tap="onRemoveWordTap_"
+                  tabindex$="[[tabIndex]]">
+              </paper-icon-button>
+            </div>
+          </template>
+        </iron-list>
+      </template>
+      <template is="dom-if" if="[[!hasWords_(words_.length)]]">
+        <div id="noWordsLabel" class="list-item">
+          $i18n{noCustomDictionaryWordsFound}
+        </div>
+      </template>
     </div>
   </template>
   <script src="edit_dictionary_page.js"></script>
diff --git a/chrome/browser/resources/settings/people_page/users_page.html b/chrome/browser/resources/settings/people_page/users_page.html
index a6576e12..6316f4a 100644
--- a/chrome/browser/resources/settings/people_page/users_page.html
+++ b/chrome/browser/resources/settings/people_page/users_page.html
@@ -22,6 +22,8 @@
 
       #add-user-button {
         /* Add user button must be lined up with the start of users' names. */
+        /* TODO(dschuyler): Should this be var(--settings-box-row-indent) rather
+           than 56px? */
         -webkit-margin-start: 56px;
       }
     </style>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html
index ed7e766..aedba645 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.html
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -24,10 +24,6 @@
         padding-top: 8px;
       }
 
-      #headerLine > h1 {
-        -webkit-margin-start: 4px;
-      }
-
       #learnMore {
         @apply(--cr-paper-icon-button-margin);
         align-items: center;
@@ -40,8 +36,8 @@
 
       paper-icon-button {
         /* Centers the ripple on the icon with appropriate margin on right. */
-        -webkit-margin-end: 8px;
-        -webkit-margin-start: -8px;
+        -webkit-margin-end: 10px;
+        -webkit-margin-start: -10px;
       }
 
       paper-spinner {
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 9d0e6a13..9283f0dd 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -134,7 +134,7 @@
 
       paper-radio-button {
         --paper-radio-button-checked-color: var(--google-blue-500);
-        --paper-radio-button-label-spacing: 18px;
+        --paper-radio-button-label-spacing: 22px;
         --paper-radio-button-unchecked-color: var(--paper-grey-600);
         -webkit-margin-start: 2px;
         align-items: center;
@@ -178,7 +178,7 @@
       }
 
       /* A list-item is intended to be contained within a list-frame. The list
-       * frame will setup the initial start margin. */
+       * frame will set up the initial start margin. */
       .list-item {
         align-items: center;
         display: flex;
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html
index 2a739636..a0b7962 100644
--- a/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -16,7 +16,7 @@
     --settings-box-row-padding: 20px;
     --settings-box-row-indent: calc(
         var(--settings-box-row-padding) + var(--settings-indent-width));
-    --settings-indent-width: 36px;
+    --settings-indent-width: 40px;
     --settings-card-max-width: 680px;
     --settings-disabled-opacity: .65;
     --settings-error-color: var(--paper-red-700);
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
index 2d03697..ca47f6a 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
@@ -26,6 +26,11 @@
         --paper-checkbox-unchecked-color: var(--paper-grey-600);
         -webkit-margin-start: 2px;
       }
+<if expr="is_macosx">
+      html:not(.focus-allowed) paper-checkbox {
+        --paper-checkbox-ink-size: 0;
+      }
+</if>
     </style>
   </head>
   <body>
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
index c7f9c10..bf360039 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
@@ -32,6 +32,7 @@
 
   function clearFocus() {
     document.activeElement.blur();
+    document.documentElement.classList.add('focus-allowed');
   }
 
   function setUserImageURL(url) {
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index b90de69..ccf25e0 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -20,9 +20,11 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/force_signin_verifier.h"
 #include "chrome/browser/signin/local_auth.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/user_manager.h"
 #include "chrome/browser/web_data_service_factory.h"
@@ -58,22 +60,12 @@
 #include "chrome/browser/first_run/first_run.h"
 #endif
 
-namespace {
-
-bool IsForceSigninEnabled() {
-  PrefService* prefs = g_browser_process->local_state();
-  return prefs && prefs->GetBoolean(prefs::kForceBrowserSignin);
-}
-
-}  // namespace
-
 ChromeSigninClient::ChromeSigninClient(
     Profile* profile,
     SigninErrorController* signin_error_controller)
     : OAuth2TokenService::Consumer("chrome_signin_client"),
       profile_(profile),
-      signin_error_controller_(signin_error_controller),
-      is_force_signin_enabled_(IsForceSigninEnabled()) {
+      signin_error_controller_(signin_error_controller) {
   signin_error_controller_->AddObserver(this);
 #if !defined(OS_CHROMEOS)
   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
@@ -118,6 +110,7 @@
 
 void ChromeSigninClient::DoFinalInit() {
   MaybeFetchSigninTokenHandle();
+  VerifySyncToken();
 }
 
 // static
@@ -280,7 +273,7 @@
     const base::Callback<void()>& sign_out,
     signin_metrics::ProfileSignout signout_source_metric) {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  if (is_force_signin_enabled_ && !profile_->IsSystemProfile() &&
+  if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
       !profile_->IsGuestSession() && !profile_->IsSupervised()) {
     // TODO(zmin): force window closing based on the reason of sign-out.
     // This will be updated after force window closing CL is commited.
@@ -399,6 +392,13 @@
   return base::MakeUnique<GaiaAuthFetcher>(consumer, source, getter);
 }
 
+void ChromeSigninClient::VerifySyncToken() {
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  if (signin_util::IsForceSigninEnabled())
+    force_signin_verifier_ = base::MakeUnique<ForceSigninVerifier>(profile_);
+#endif
+}
+
 void ChromeSigninClient::MaybeFetchSigninTokenHandle() {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   // We get a "handle" that can be used to reference the signin token on the
@@ -428,7 +428,7 @@
 }
 
 void ChromeSigninClient::AfterCredentialsCopied() {
-  if (is_force_signin_enabled_) {
+  if (signin_util::IsForceSigninEnabled()) {
     // The signout after credential copy won't open UserManager after all
     // browser window are closed. Because the browser window will be opened for
     // the new profile soon.
@@ -440,6 +440,10 @@
     const base::Callback<void()>& sign_out,
     const signin_metrics::ProfileSignout signout_source_metric,
     const base::FilePath& profile_path) {
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get())
+    force_signin_verifier_->Cancel();
+#endif
   SigninClient::PreSignOut(sign_out, signout_source_metric);
 
   LockForceSigninProfile(profile_path);
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index 430cb69..57a8220 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -17,6 +17,9 @@
 #include "net/base/network_change_notifier.h"
 #endif
 
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+class ForceSigninVerifier;
+#endif
 class Profile;
 
 class ChromeSigninClient
@@ -107,6 +110,7 @@
 
  private:
   void MaybeFetchSigninTokenHandle();
+  void VerifySyncToken();
   void OnCloseBrowsersSuccess(
       const base::Callback<void()>& sign_out,
       const signin_metrics::ProfileSignout signout_source_metric,
@@ -120,8 +124,10 @@
   std::list<base::Closure> delayed_callbacks_;
 #endif
 
-  bool is_force_signin_enabled_;
   bool should_display_user_manager_ = true;
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  std::unique_ptr<ForceSigninVerifier> force_signin_verifier_;
+#endif
 
   std::unique_ptr<gaia::GaiaOAuthClient> oauth_client_;
   std::unique_ptr<OAuth2TokenService::Request> oauth_request_;
diff --git a/chrome/browser/signin/chrome_signin_client_unittest.cc b/chrome/browser/signin/chrome_signin_client_unittest.cc
index 7d40db7..16d57bb 100644
--- a/chrome/browser/signin/chrome_signin_client_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_client_unittest.cc
@@ -10,15 +10,14 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
-#include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/prefs/testing_pref_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/network_change_notifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -153,11 +152,7 @@
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
 
-    prefs_.reset(new TestingPrefServiceSimple());
-    chrome::RegisterLocalState(prefs_->registry());
-    TestingBrowserProcess::GetGlobal()->SetLocalState(prefs_.get());
-    prefs_->SetBoolean(prefs::kForceBrowserSignin, true);
-
+    signin_util::SetForceSigninForTesting(true);
     CreateClient(browser()->profile());
     manager_.reset(new MockSigninManager(client_.get()));
   }
@@ -176,7 +171,6 @@
   std::unique_ptr<SigninErrorController> fake_controller_;
   std::unique_ptr<MockChromeSigninClient> client_;
   std::unique_ptr<MockSigninManager> manager_;
-  std::unique_ptr<TestingPrefServiceSimple> prefs_;
 };
 
 TEST_F(ChromeSigninClientSignoutTest, SignOut) {
@@ -221,7 +215,7 @@
 }
 
 TEST_F(ChromeSigninClientSignoutTest, SignOutWithoutForceSignin) {
-  prefs_->SetBoolean(prefs::kForceBrowserSignin, false);
+  signin_util::SetForceSigninForTesting(false);
   CreateClient(browser()->profile());
   manager_.reset(new MockSigninManager(client_.get()));
 
diff --git a/chrome/browser/signin/force_signin_verifier.cc b/chrome/browser/signin/force_signin_verifier.cc
new file mode 100644
index 0000000..9afd378
--- /dev/null
+++ b/chrome/browser/signin/force_signin_verifier.cc
@@ -0,0 +1,124 @@
+// 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 <string>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/force_signin_verifier.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "google_apis/gaia/gaia_constants.h"
+
+namespace {
+const net::BackoffEntry::Policy kBackoffPolicy = {
+    0,              // Number of initial errors to ignore before applying
+                    // exponential back-off rules.
+    2000,           // Initial delay in ms.
+    2,              // Factor by which the waiting time will be multiplied.
+    0.2,            // Fuzzing percentage.
+    4 * 60 * 1000,  // Maximum amount of time to delay th request in ms.
+    -1,             // Never discard the entry.
+    false           // Do not always use initial delay.
+};
+
+}  // namespace
+
+ForceSigninVerifier::ForceSigninVerifier(Profile* profile)
+    : OAuth2TokenService::Consumer("force_signin_verifier"),
+      has_token_verified_(false),
+      backoff_entry_(&kBackoffPolicy),
+      oauth2_token_service_(
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
+      signin_manager_(SigninManagerFactory::GetForProfile(profile)),
+      token_request_time_(base::Time::Now()) {
+  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+  SendRequest();
+}
+
+ForceSigninVerifier::~ForceSigninVerifier() {
+  Cancel();
+}
+
+void ForceSigninVerifier::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const std::string& access_token,
+    const base::Time& expiration_time) {
+  has_token_verified_ = true;
+  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+  Cancel();
+}
+
+void ForceSigninVerifier::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  if (error.IsPersistentError()) {
+    has_token_verified_ = true;
+    ShowDialog();
+    net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+    Cancel();
+  } else {
+    backoff_entry_.InformOfRequest(false);
+    backoff_request_timer_.Start(
+        FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
+        base::Bind(&ForceSigninVerifier::SendRequest, base::Unretained(this)));
+    access_token_request_.reset();
+  }
+}
+
+void ForceSigninVerifier::OnNetworkChanged(
+    net::NetworkChangeNotifier::ConnectionType type) {
+  // Try again immediately once the network is back and cancel any pending
+  // request.
+  backoff_entry_.Reset();
+  if (backoff_request_timer_.IsRunning())
+    backoff_request_timer_.Stop();
+
+  if (type != net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE)
+    SendRequest();
+}
+
+void ForceSigninVerifier::Cancel() {
+  backoff_entry_.Reset();
+  backoff_request_timer_.Stop();
+  access_token_request_.reset();
+  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+}
+
+bool ForceSigninVerifier::HasTokenBeenVerified() {
+  return has_token_verified_;
+}
+
+void ForceSigninVerifier::SendRequest() {
+  if (!ShouldSendRequest())
+    return;
+
+  std::string account_id = signin_manager_->GetAuthenticatedAccountId();
+  OAuth2TokenService::ScopeSet oauth2_scopes;
+  oauth2_scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
+  access_token_request_ =
+      oauth2_token_service_->StartRequest(account_id, oauth2_scopes, this);
+}
+
+bool ForceSigninVerifier::ShouldSendRequest() {
+  return !has_token_verified_ && access_token_request_.get() == nullptr &&
+         !net::NetworkChangeNotifier::IsOffline() &&
+         signin_manager_->IsAuthenticated();
+}
+
+void ForceSigninVerifier::ShowDialog() {
+  // TODO(zmin): Show app modal dialog.
+}
+
+OAuth2TokenService::Request* ForceSigninVerifier::GetRequestForTesting() {
+  return access_token_request_.get();
+}
+
+net::BackoffEntry* ForceSigninVerifier::GetBackoffEntryForTesting() {
+  return &backoff_entry_;
+}
+
+base::OneShotTimer* ForceSigninVerifier::GetOneShotTimerForTesting() {
+  return &backoff_request_timer_;
+}
diff --git a/chrome/browser/signin/force_signin_verifier.h b/chrome/browser/signin/force_signin_verifier.h
new file mode 100644
index 0000000..03548c03
--- /dev/null
+++ b/chrome/browser/signin/force_signin_verifier.h
@@ -0,0 +1,84 @@
+// 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_SIGNIN_FORCE_SIGNIN_VERIFIER_H_
+#define CHROME_BROWSER_SIGNIN_FORCE_SIGNIN_VERIFIER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "net/base/backoff_entry.h"
+#include "net/base/network_change_notifier.h"
+
+class Profile;
+class SigninManager;
+
+// ForceSigninVerifier will verify profile's auth token when profile is loaded
+// into memory by the first time via gaia server. It will retry on any transient
+// error.
+class ForceSigninVerifier
+    : public OAuth2TokenService::Consumer,
+      public net::NetworkChangeNotifier::NetworkChangeObserver {
+ public:
+  explicit ForceSigninVerifier(Profile* profile);
+  ~ForceSigninVerifier() override;
+
+  // OAuth2TokenService::Consumer implementation
+  void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+                         const std::string& access_token,
+                         const base::Time& expiration_time) override;
+  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                         const GoogleServiceAuthError& error) override;
+
+  // net::NetworkChangeNotifier::NetworkChangeObserver
+  void OnNetworkChanged(
+      net::NetworkChangeNotifier::ConnectionType type) override;
+
+  // Cancel any pending or ongoing verification.
+  void Cancel();
+
+  // Return the value of |has_token_verified_|.
+  bool HasTokenBeenVerified();
+
+ protected:
+  // Send the token verification request. The request will be sent only if
+  //   - The token has never been verified before.
+  //   - There is no on going verification.
+  //   - There is network connection.
+  //   - The profile has signed in.
+  //
+  void SendRequest();
+
+  virtual bool ShouldSendRequest();
+
+  // Show the warning dialog before signing out user and closing assoicated
+  // browser window.
+  virtual void ShowDialog();
+
+  OAuth2TokenService::Request* GetRequestForTesting();
+  net::BackoffEntry* GetBackoffEntryForTesting();
+  base::OneShotTimer* GetOneShotTimerForTesting();
+
+ private:
+  std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
+
+  // Indicates whether the verification is finished successfully or with a
+  // persistent error.
+  bool has_token_verified_;
+  net::BackoffEntry backoff_entry_;
+  base::OneShotTimer backoff_request_timer_;
+
+  OAuth2TokenService* oauth2_token_service_;
+  SigninManager* signin_manager_;
+
+  base::Time token_request_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForceSigninVerifier);
+};
+
+#endif  // CHROME_BROWSER_SIGNIN_FORCE_SIGNIN_VERIFIER_H_
diff --git a/chrome/browser/signin/force_signin_verifier_unittest.cc b/chrome/browser/signin/force_signin_verifier_unittest.cc
new file mode 100644
index 0000000..d5f2f555
--- /dev/null
+++ b/chrome/browser/signin/force_signin_verifier_unittest.cc
@@ -0,0 +1,126 @@
+// 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/signin/force_signin_verifier.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class MockForceSigninVerifier : public ForceSigninVerifier {
+ public:
+  explicit MockForceSigninVerifier(Profile* profile)
+      : ForceSigninVerifier(profile) {}
+
+  bool ShouldSendRequest() override { return true; }
+
+  void SendTestRequest() { SendRequest(); }
+
+  bool IsDelayTaskPosted() { return GetOneShotTimerForTesting()->IsRunning(); }
+
+  int FailureCount() { return GetBackoffEntryForTesting()->failure_count(); }
+
+  OAuth2TokenService::Request* request() { return GetRequestForTesting(); }
+
+  MOCK_METHOD0(ShowDialog, void(void));
+};
+
+class ForceSigninVerifierTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    verifier_ = base::MakeUnique<MockForceSigninVerifier>(&profile_);
+  }
+
+  void TearDown() override { verifier_.reset(); }
+
+  std::unique_ptr<MockForceSigninVerifier> verifier_;
+  content::TestBrowserThreadBundle bundle_;
+  TestingProfile profile_;
+
+  GoogleServiceAuthError persistent_error_ = GoogleServiceAuthError(
+      GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS);
+  GoogleServiceAuthError transient_error_ =
+      GoogleServiceAuthError(GoogleServiceAuthError::State::CONNECTION_FAILED);
+};
+
+TEST_F(ForceSigninVerifierTest, OnGetTokenSuccess) {
+  ASSERT_EQ(nullptr, verifier_->request());
+
+  verifier_->SendTestRequest();
+
+  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->HasTokenBeenVerified());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+  EXPECT_CALL(*verifier_.get(), ShowDialog()).Times(0);
+
+  verifier_->OnGetTokenSuccess(verifier_->request(), "", base::Time::Now());
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_TRUE(verifier_->HasTokenBeenVerified());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+  ASSERT_EQ(0, verifier_->FailureCount());
+}
+
+TEST_F(ForceSigninVerifierTest, OnGetTokenPersistentFailure) {
+  ASSERT_EQ(nullptr, verifier_->request());
+
+  verifier_->SendTestRequest();
+
+  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->HasTokenBeenVerified());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+  EXPECT_CALL(*verifier_.get(), ShowDialog()).Times(1);
+
+  verifier_->OnGetTokenFailure(verifier_->request(), persistent_error_);
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_TRUE(verifier_->HasTokenBeenVerified());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+  ASSERT_EQ(0, verifier_->FailureCount());
+}
+
+TEST_F(ForceSigninVerifierTest, OnGetTokenTransientFailure) {
+  ASSERT_EQ(nullptr, verifier_->request());
+
+  verifier_->SendTestRequest();
+  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->HasTokenBeenVerified());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+  EXPECT_CALL(*verifier_.get(), ShowDialog()).Times(0);
+
+  verifier_->OnGetTokenFailure(verifier_->request(), transient_error_);
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->HasTokenBeenVerified());
+  ASSERT_TRUE(verifier_->IsDelayTaskPosted());
+  ASSERT_EQ(1, verifier_->FailureCount());
+}
+
+TEST_F(ForceSigninVerifierTest, OnLostConnection) {
+  verifier_->SendTestRequest();
+  verifier_->OnGetTokenFailure(verifier_->request(), transient_error_);
+  ASSERT_EQ(1, verifier_->FailureCount());
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_TRUE(verifier_->IsDelayTaskPosted());
+
+  verifier_->OnNetworkChanged(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
+
+  ASSERT_EQ(0, verifier_->FailureCount());
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+}
+
+TEST_F(ForceSigninVerifierTest, OnReconnected) {
+  verifier_->SendTestRequest();
+  verifier_->OnGetTokenFailure(verifier_->request(), transient_error_);
+  ASSERT_EQ(1, verifier_->FailureCount());
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_TRUE(verifier_->IsDelayTaskPosted());
+
+  verifier_->OnNetworkChanged(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+
+  ASSERT_EQ(0, verifier_->FailureCount());
+  ASSERT_NE(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->IsDelayTaskPosted());
+}
diff --git a/chrome/browser/signin/signin_util.cc b/chrome/browser/signin/signin_util.cc
index e5f50790..7932788 100644
--- a/chrome/browser/signin/signin_util.cc
+++ b/chrome/browser/signin/signin_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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.
 
diff --git a/chrome/browser/signin/signin_util.h b/chrome/browser/signin/signin_util.h
index 315fc63..0c6ad88f 100644
--- a/chrome/browser/signin/signin_util.h
+++ b/chrome/browser/signin/signin_util.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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.
 
diff --git a/chrome/browser/signin/signin_util_unittest.cc b/chrome/browser/signin/signin_util_unittest.cc
index d436f78..0233f99 100644
--- a/chrome/browser/signin/signin_util_unittest.cc
+++ b/chrome/browser/signin/signin_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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.
 
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc
index 45e970c..3f9ddae 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc
@@ -56,17 +56,16 @@
   target->OnMessageReceived(to_be_received);
 
   run_loop.Run();
-  EXPECT_EQ(1U, target->sent_messages_.size());
+  ASSERT_EQ(1U, target->sent_messages_.size());
 
   SpellCheckMsg_RespondTextCheck::Param params;
   bool ok = SpellCheckMsg_RespondTextCheck::Read(
       target->sent_messages_[0].get(), &params);
-  std::vector<SpellCheckResult> sent_results = std::get<2>(params);
-
   EXPECT_TRUE(ok);
-  EXPECT_EQ(1U, sent_results.size());
+
+  std::vector<SpellCheckResult> sent_results = std::get<2>(params);
+  ASSERT_EQ(1U, sent_results.size());
   EXPECT_EQ(sent_results[0].location, 0);
   EXPECT_EQ(sent_results[0].length, 2);
-  EXPECT_EQ(sent_results[0].decoration,
-            SpellCheckResult::SPELLING);
+  EXPECT_EQ(sent_results[0].decoration, SpellCheckResult::SPELLING);
 }
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
index 83bb639..62d445f9 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
@@ -104,7 +104,7 @@
       new TestingSpellCheckMessageFilter);
   filter->GetSpellcheckService()->GetCustomDictionary()->AddWord(kCustomWord);
   filter->OnTextCheckComplete(kRouteId, kCallbackId, kSuccess, kText, results);
-  EXPECT_EQ(static_cast<size_t>(1), filter->sent_messages.size());
+  ASSERT_EQ(1U, filter->sent_messages.size());
 
   SpellCheckMsg_RespondSpellingService::Param params;
   bool ok = SpellCheckMsg_RespondSpellingService::Read(
@@ -117,7 +117,7 @@
   EXPECT_EQ(kCallbackId, sent_identifier);
   EXPECT_EQ(kSuccess, sent_success);
   EXPECT_EQ(kText, sent_text);
-  EXPECT_EQ(static_cast<size_t>(1), sent_results.size());
+  ASSERT_EQ(1U, sent_results.size());
   EXPECT_EQ(kDecoration, sent_results[0].decoration);
   EXPECT_EQ(kLocation, sent_results[0].location);
   EXPECT_EQ(kLength, sent_results[0].length);
@@ -135,14 +135,14 @@
       new TestingSpellCheckMessageFilter);
   filter->OnTextCheckComplete(1, 1, true, base::ASCIIToUTF16("Helllo walrd"),
                               results);
-  EXPECT_EQ(static_cast<size_t>(1), filter->sent_messages.size());
+  ASSERT_EQ(1U, filter->sent_messages.size());
 
   SpellCheckMsg_RespondSpellingService::Param params;
   bool ok = SpellCheckMsg_RespondSpellingService::Read(
       filter->sent_messages[0].get(), &params);
-  base::string16 sent_text = std::get<2>(params);
-  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
   EXPECT_TRUE(ok);
-  EXPECT_EQ(static_cast<size_t>(2), sent_results.size());
+
+  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
+  EXPECT_EQ(2U, sent_results.size());
 }
 #endif
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index d07712f..e496d89 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -127,7 +127,7 @@
   for (const auto& value :
        *prefs->GetList(spellcheck::prefs::kSpellCheckDictionaries)) {
     std::string dictionary;
-    if (value->GetAsString(&dictionary))
+    if (value.GetAsString(&dictionary))
       spellcheck_dictionaries.insert(dictionary);
   }
 
@@ -216,7 +216,7 @@
 
   for (const auto& dictionary_value : *dictionary_values) {
     std::string dictionary;
-    dictionary_value->GetAsString(&dictionary);
+    dictionary_value.GetAsString(&dictionary);
     hunspell_dictionaries_.push_back(
         base::MakeUnique<SpellcheckHunspellDictionary>(
             dictionary,
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index ca047ba..0c3895a 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -133,7 +133,7 @@
     std::vector<base::StringPiece> dictionaries;
     for (const auto& item_value : *list_value) {
       base::StringPiece dictionary;
-      EXPECT_TRUE(item_value->GetAsString(&dictionary));
+      EXPECT_TRUE(item_value.GetAsString(&dictionary));
       dictionaries.push_back(dictionary);
     }
     return base::JoinString(dictionaries, ",");
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
index 35f06fc..41ea8ef 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -268,8 +268,8 @@
        it != list->end();
        it++) {
     FamilyMember member;
-    base::DictionaryValue* dict = NULL;
-    if (!(*it)->GetAsDictionary(&dict) || !ParseMember(dict, &member)) {
+    const base::DictionaryValue* dict = NULL;
+    if (!it->GetAsDictionary(&dict) || !ParseMember(dict, &member)) {
       return false;
     }
     members->push_back(member);
diff --git a/chrome/browser/supervised_user/supervised_user_site_list.cc b/chrome/browser/supervised_user/supervised_user_site_list.cc
index 5d6c0627..8b9253b2 100644
--- a/chrome/browser/supervised_user/supervised_user_site_list.cc
+++ b/chrome/browser/supervised_user/supervised_user_site_list.cc
@@ -48,7 +48,7 @@
   if (list_values) {
     for (const auto& entry : *list_values) {
       std::string entry_string;
-      if (!entry->GetAsString(&entry_string)) {
+      if (!entry.GetAsString(&entry_string)) {
         LOG(ERROR) << "Invalid whitelist entry";
         continue;
       }
diff --git a/chrome/browser/sync/test/integration/preferences_helper.cc b/chrome/browser/sync/test/integration/preferences_helper.cc
index 3989df9..3b50d009 100644
--- a/chrome/browser/sync/test/integration/preferences_helper.cc
+++ b/chrome/browser/sync/test/integration/preferences_helper.cc
@@ -77,7 +77,7 @@
     for (base::ListValue::const_iterator it = new_value.begin();
          it != new_value.end();
          ++it) {
-      list->Append((*it)->CreateDeepCopy());
+      list->Append(it->CreateDeepCopy());
     }
   }
 
@@ -87,7 +87,7 @@
     for (base::ListValue::const_iterator it = new_value.begin();
          it != new_value.end();
          ++it) {
-      list_verifier->Append((*it)->CreateDeepCopy());
+      list_verifier->Append(it->CreateDeepCopy());
     }
   }
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 81f24e8..f22a347 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -647,6 +647,13 @@
     defines += [ "USE_CRAS" ]
   }
 
+  if (is_android || is_linux) {
+    sources += [
+      "webui/sandbox_internals_ui.cc",
+      "webui/sandbox_internals_ui.h",
+    ]
+  }
+
   if (!is_android) {
     sources += [
       "apps/app_info_dialog.h",
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
index 12b0c575..7999174d 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
@@ -139,7 +139,7 @@
        it != result_list->end();
        ++it) {
     const base::DictionaryValue* dict;
-    if (!(*it)->GetAsDictionary(&dict))
+    if (!it->GetAsDictionary(&dict))
       continue;
 
     std::unique_ptr<SearchResult> result(CreateResult(query, *dict));
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc
index d57b18a..c9b49de7 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc
@@ -137,8 +137,15 @@
 
 // This test checks that the browser doesn't crash if the delegate is deleted
 // before the popup is hidden.
+#if defined(OS_MACOSX)
+// Flaky on Mac 10.9 in debug mode. http://crbug.com/710439
+#define MAYBE_DeleteDelegateBeforePopupHidden \
+  DISABLED_DeleteDelegateBeforePopupHidden
+#else
+#define MAYBE_DeleteDelegateBeforePopupHidden DeleteDelegateBeforePopupHidden
+#endif
 IN_PROC_BROWSER_TEST_F(AutofillPopupControllerBrowserTest,
-                       DeleteDelegateBeforePopupHidden){
+                       MAYBE_DeleteDelegateBeforePopupHidden) {
   GenerateTestAutofillPopup(autofill_external_delegate_.get());
 
   // Delete the external delegate here so that is gets deleted before popup is
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
index 5eff702..38db688 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
@@ -70,6 +70,7 @@
     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     folder_image_.reset(
         rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).CopyNSImage());
+    [folder_image_ setTemplate:YES];
   }
 
   ClearBookmarkMenu(bookmark_menu);
@@ -355,6 +356,7 @@
   if (!favicon) {
     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     favicon = rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage();
+    [favicon setTemplate:YES];
   }
   [item setImage:favicon];
 }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index e48835e..4c51a81 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -474,22 +474,6 @@
     return true;
   }
 
-  bool DragInputToDelayedNotifyWhenDone(int x,
-                                        int y,
-                                        const base::Closure& task,
-                                        base::TimeDelta delay) {
-    if (input_source() == INPUT_SOURCE_MOUSE)
-      return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
-#if defined(OS_CHROMEOS)
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task,
-                                                         delay);
-    event_generator_->MoveTouch(gfx::Point(x, y));
-#else
-    NOTREACHED();
-#endif
-    return true;
-  }
-
   bool DragInput2ToNotifyWhenDone(int x,
                                  int y,
                                  const base::Closure& task) {
@@ -2361,7 +2345,7 @@
 // Drags from browser from a second display to primary and releases input.
 IN_PROC_BROWSER_TEST_F(
     DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
-    DISABLED_CancelDragTabToWindowIn1stDisplay) {
+    CancelDragTabToWindowIn1stDisplay) {
   aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
   ASSERT_EQ(2u, roots.size());
 
@@ -2406,45 +2390,63 @@
 }
 
 namespace {
-
-void PressSecondFingerWhileDetachedStep2(
+void PressSecondFingerWhileDetachedStep3(
     DetachToBrowserTabDragControllerTest* test) {
   ASSERT_TRUE(TabDragController::IsActive());
   ASSERT_EQ(2u, test->browser_list->size());
-  Browser* new_browser = test->browser_list->get(1);
-  ASSERT_TRUE(new_browser->window()->IsActive());
+  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
 
+  ASSERT_TRUE(test->ReleaseInput());
+  ASSERT_TRUE(test->ReleaseInput2());
+}
+
+void PressSecondFingerWhileDetachedStep2(
+    DetachToBrowserTabDragControllerTest* test,
+    const gfx::Point& target_point) {
+  ASSERT_TRUE(TabDragController::IsActive());
+  ASSERT_EQ(2u, test->browser_list->size());
+  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
+
+  // Continue dragging after adding a second finger.
   ASSERT_TRUE(test->PressInput2());
+  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+      target_point.x(), target_point.y(),
+      base::Bind(&PressSecondFingerWhileDetachedStep3, test)));
 }
 
 }  // namespace
 
 // Detaches a tab and while detached presses a second finger.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
-                       DISABLED_PressSecondFingerWhileDetached) {
+                       PressSecondFingerWhileDetached) {
   // Add another tab.
   AddTabAndResetBrowser(browser());
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
   ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToDelayedNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&PressSecondFingerWhileDetachedStep2, this),
-                  base::TimeDelta::FromMilliseconds(60)));
+  ASSERT_TRUE(DragInputToNotifyWhenDone(
+      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
+      base::Bind(&PressSecondFingerWhileDetachedStep2, this,
+                 gfx::Point(tab_0_center.x(),
+                            tab_0_center.y() + 2 * GetDetachY(tab_strip)))));
   QuitWhenNotDragging();
 
-  // The drag should have been reverted.
-  ASSERT_EQ(1u, browser_list->size());
+  // Should no longer be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
   ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
 
-  ASSERT_TRUE(ReleaseInput());
-  ASSERT_TRUE(ReleaseInput2());
+  // There should now be another browser.
+  ASSERT_EQ(2u, browser_list->size());
+  Browser* new_browser = browser_list->get(1);
+  ASSERT_TRUE(new_browser->window()->IsActive());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+
+  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
 }
 
 #endif  // OS_CHROMEOS
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index 707b769..ecf2bc8 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -77,11 +77,6 @@
 #include "chrome/browser/ui/webui/theme_source.h"
 #endif
 
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-#include "content/public/browser/zygote_host_linux.h"
-#include "content/public/common/sandbox_linux.h"
-#endif
-
 #if defined(OS_WIN)
 #include "chrome/browser/win/enumerate_modules_model.h"
 #endif
@@ -655,78 +650,10 @@
   data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
   AppendBody(&data);
   base::FilePath binary = base::CommandLine::ForCurrentProcess()->GetProgram();
-  data.append(l10n_util::GetStringFUTF8(
-      IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
-      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
-      base::ASCIIToUTF16(binary.BaseName().value())));
-  AppendFooter(&data);
-  return data;
-}
-
-void AboutSandboxRow(std::string* data, int name_id, bool good) {
-  data->append("<tr><td>");
-  data->append(l10n_util::GetStringUTF8(name_id));
-  if (good) {
-    data->append("</td><td style='color: green;'>");
-    data->append(
-        l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
-  } else {
-    data->append("</td><td style='color: red;'>");
-    data->append(
-        l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
-  }
-  data->append("</td></tr>");
-}
-
-std::string AboutSandbox() {
-  std::string data;
-  AppendHeader(&data, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
-  AppendBody(&data);
-  data.append("<h1>");
-  data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
-  data.append("</h1>");
-
-  // Get expected sandboxing status of renderers.
-  const int status =
-      content::ZygoteHost::GetInstance()->GetRendererSandboxStatus();
-
-  data.append("<table>");
-
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_SUID_SANDBOX,
-                  status & content::kSandboxLinuxSUID);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_NAMESPACE_SANDBOX,
-                  status & content::kSandboxLinuxUserNS);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_PID_NAMESPACES,
-                  status & content::kSandboxLinuxPIDNS);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_NET_NAMESPACES,
-                  status & content::kSandboxLinuxNetNS);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX,
-                  status & content::kSandboxLinuxSeccompBPF);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC,
-                  status & content::kSandboxLinuxSeccompTSYNC);
-  AboutSandboxRow(&data, IDS_ABOUT_SANDBOX_YAMA_LSM,
-                  status & content::kSandboxLinuxYama);
-
-  data.append("</table>");
-
-  // Require either the setuid or namespace sandbox for our first-layer sandbox.
-  bool good_layer1 = (status & content::kSandboxLinuxSUID ||
-                      status & content::kSandboxLinuxUserNS) &&
-                     status & content::kSandboxLinuxPIDNS &&
-                     status & content::kSandboxLinuxNetNS;
-  // A second-layer sandbox is also required to be adequately sandboxed.
-  bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
-  bool good = good_layer1 && good_layer2;
-
-  if (good) {
-    data.append("<p style='color: green'>");
-    data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
-  } else {
-    data.append("<p style='color: red'>");
-    data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
-  }
-  data.append("</p>");
-
+  data.append(
+      l10n_util::GetStringFUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
+                                l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+                                base::ASCIIToUTF16(binary.BaseName().value())));
   AppendFooter(&data);
   return data;
 }
@@ -806,10 +733,6 @@
     ChromeOSCreditsHandler::Start(path, callback);
     return;
 #endif
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-  } else if (source_name_ == chrome::kChromeUISandboxHost) {
-    response = AboutSandbox();
-#endif
 #if !defined(OS_ANDROID)
   } else if (source_name_ == chrome::kChromeUITermsHost) {
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 87b3e8d..046516d 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -364,14 +364,13 @@
   items_to_remove.reserve(args->GetSize());
   for (base::ListValue::const_iterator it = args->begin();
        it != args->end(); ++it) {
-    base::DictionaryValue* deletion = NULL;
+    const base::DictionaryValue* deletion = NULL;
     base::string16 url;
-    base::ListValue* timestamps = NULL;
+    const base::ListValue* timestamps = NULL;
 
     // Each argument is a dictionary with properties "url" and "timestamps".
-    if (!((*it)->GetAsDictionary(&deletion) &&
-        deletion->GetString("url", &url) &&
-        deletion->GetList("timestamps", &timestamps))) {
+    if (!(it->GetAsDictionary(&deletion) && deletion->GetString("url", &url) &&
+          deletion->GetList("timestamps", &timestamps))) {
       NOTREACHED() << "Unable to extract arguments";
       return;
     }
@@ -384,7 +383,7 @@
     double timestamp;
     for (base::ListValue::const_iterator ts_iterator = timestamps->begin();
          ts_iterator != timestamps->end(); ++ts_iterator) {
-      if (!(*ts_iterator)->GetAsDouble(&timestamp)) {
+      if (!ts_iterator->GetAsDouble(&timestamp)) {
         NOTREACHED() << "Unable to extract visit timestamp.";
         continue;
       }
diff --git a/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chrome/browser/ui/webui/certificate_viewer_webui.cc
index 1ab60ce8..eebe9f1 100644
--- a/chrome/browser/ui/webui/certificate_viewer_webui.cc
+++ b/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -291,74 +291,55 @@
 
   net::X509Certificate::OSCertHandle cert = cert_chain_[cert_index];
 
-  base::ListValue root_list;
-  base::DictionaryValue* node_details;
-  base::DictionaryValue* alt_node_details;
-  base::ListValue* cert_sub_fields;
-  root_list.Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label", x509_certificate_model::GetTitle(cert));
-
-  base::ListValue* cert_fields;
-  node_details->Set("children", cert_fields = new base::ListValue());
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE));
-  node_details->Set("children", cert_fields = new base::ListValue());
-
   // Main certificate fields.
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  auto cert_fields = base::MakeUnique<base::ListValue>();
+  auto node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VERSION));
   std::string version = x509_certificate_model::GetVersion(cert);
-  if (!version.empty())
+  if (!version.empty()) {
     node_details->SetString("payload.val",
         l10n_util::GetStringFUTF8(IDS_CERT_DETAILS_VERSION_FORMAT,
                                   base::UTF8ToUTF16(version)));
+  }
+  cert_fields->Append(std::move(node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SERIAL_NUMBER));
   node_details->SetString("payload.val",
       x509_certificate_model::GetSerialNumberHexified(cert,
           l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT)));
+  cert_fields->Append(std::move(node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG));
   node_details->SetString("payload.val",
       x509_certificate_model::ProcessSecAlgorithmSignature(cert));
+  cert_fields->Append(std::move(node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_ISSUER));
   node_details->SetString("payload.val",
       x509_certificate_model::GetIssuerName(cert));
+  cert_fields->Append(std::move(node_details));
 
   // Validity period.
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VALIDITY));
+  auto cert_sub_fields = base::MakeUnique<base::ListValue>();
 
-  node_details->Set("children", cert_sub_fields = new base::ListValue());
-  cert_sub_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_BEFORE));
-  cert_sub_fields->Append(
-      base::WrapUnique(alt_node_details = new base::DictionaryValue()));
+  auto sub_node_details = base::MakeUnique<base::DictionaryValue>();
+  sub_node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_BEFORE));
+
+  auto alt_node_details = base::MakeUnique<base::DictionaryValue>();
   alt_node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_AFTER));
+
   base::Time issued, expires;
   if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
-    node_details->SetString(
+    sub_node_details->SetString(
         "payload.val",
         base::UTF16ToUTF8(
             base::TimeFormatShortDateAndTimeWithTimeZone(issued)));
@@ -367,33 +348,45 @@
         base::UTF16ToUTF8(
             base::TimeFormatShortDateAndTimeWithTimeZone(expires)));
   }
+  cert_sub_fields->Append(std::move(sub_node_details));
+  cert_sub_fields->Append(std::move(alt_node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  node_details = base::MakeUnique<base::DictionaryValue>();
+  node_details->SetString("label",
+                          l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VALIDITY));
+  node_details->Set("children", std::move(cert_sub_fields));
+  cert_fields->Append(std::move(node_details));
+
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT));
   node_details->SetString("payload.val",
       x509_certificate_model::GetSubjectName(cert));
+  cert_fields->Append(std::move(node_details));
 
   // Subject key information.
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_INFO));
+  cert_sub_fields = base::MakeUnique<base::ListValue>();
 
-  node_details->Set("children", cert_sub_fields = new base::ListValue());
-  cert_sub_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_ALG));
-  node_details->SetString("payload.val",
+  sub_node_details = base::MakeUnique<base::DictionaryValue>();
+  sub_node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_ALG));
+  sub_node_details->SetString(
+      "payload.val",
       x509_certificate_model::ProcessSecAlgorithmSubjectPublicKey(cert));
-  cert_sub_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY));
-  node_details->SetString("payload.val",
-      x509_certificate_model::ProcessSubjectPublicKeyInfo(cert));
+  cert_sub_fields->Append(std::move(sub_node_details));
+
+  sub_node_details = base::MakeUnique<base::DictionaryValue>();
+  sub_node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY));
+  sub_node_details->SetString(
+      "payload.val", x509_certificate_model::ProcessSubjectPublicKeyInfo(cert));
+  cert_sub_fields->Append(std::move(sub_node_details));
+
+  node_details = base::MakeUnique<base::DictionaryValue>();
+  node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_INFO));
+  node_details->Set("children", std::move(cert_sub_fields));
+  cert_fields->Append(std::move(node_details));
 
   // Extensions.
   x509_certificate_model::Extensions extensions;
@@ -403,53 +396,76 @@
       cert, &extensions);
 
   if (!extensions.empty()) {
-    cert_fields->Append(
-        base::WrapUnique(node_details = new base::DictionaryValue()));
-    node_details->SetString("label",
-        l10n_util::GetStringUTF8(IDS_CERT_DETAILS_EXTENSIONS));
+    cert_sub_fields = base::MakeUnique<base::ListValue>();
 
-    node_details->Set("children", cert_sub_fields = new base::ListValue());
     for (x509_certificate_model::Extensions::const_iterator i =
          extensions.begin(); i != extensions.end(); ++i) {
-      cert_sub_fields->Append(
-          base::WrapUnique(node_details = new base::DictionaryValue()));
-      node_details->SetString("label", i->name);
-      node_details->SetString("payload.val", i->value);
+      sub_node_details = base::MakeUnique<base::DictionaryValue>();
+      sub_node_details->SetString("label", i->name);
+      sub_node_details->SetString("payload.val", i->value);
+      cert_sub_fields->Append(std::move(sub_node_details));
     }
+
+    node_details = base::MakeUnique<base::DictionaryValue>();
+    node_details->SetString(
+        "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_EXTENSIONS));
+    node_details->Set("children", std::move(cert_sub_fields));
+    cert_fields->Append(std::move(node_details));
   }
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  // Details certificate information.
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG));
   node_details->SetString("payload.val",
       x509_certificate_model::ProcessSecAlgorithmSignatureWrap(cert));
+  cert_fields->Append(std::move(node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
+  node_details = base::MakeUnique<base::DictionaryValue>();
   node_details->SetString("label",
       l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_VALUE));
   node_details->SetString("payload.val",
       x509_certificate_model::ProcessRawBitsSignatureWrap(cert));
+  cert_fields->Append(std::move(node_details));
 
-  cert_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_INFO_FINGERPRINTS_GROUP));
-  node_details->Set("children", cert_sub_fields = new base::ListValue());
+  // Fingerprint information.
+  cert_sub_fields = base::MakeUnique<base::ListValue>();
 
-  cert_sub_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
+  sub_node_details = base::MakeUnique<base::DictionaryValue>();
+  sub_node_details->SetString(
+      "label",
       l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL));
-  node_details->SetString("payload.val",
-      x509_certificate_model::HashCertSHA256(cert));
-  cert_sub_fields->Append(
-      base::WrapUnique(node_details = new base::DictionaryValue()));
-  node_details->SetString("label",
-      l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL));
-  node_details->SetString("payload.val",
-      x509_certificate_model::HashCertSHA1(cert));
+  sub_node_details->SetString("payload.val",
+                              x509_certificate_model::HashCertSHA256(cert));
+  cert_sub_fields->Append(std::move(sub_node_details));
+
+  sub_node_details = base::MakeUnique<base::DictionaryValue>();
+  sub_node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL));
+  sub_node_details->SetString("payload.val",
+                              x509_certificate_model::HashCertSHA1(cert));
+  cert_sub_fields->Append(std::move(sub_node_details));
+
+  node_details = base::MakeUnique<base::DictionaryValue>();
+  node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_INFO_FINGERPRINTS_GROUP));
+  node_details->Set("children", std::move(cert_sub_fields));
+  cert_fields->Append(std::move(node_details));
+
+  // Certificate information.
+  node_details = base::MakeUnique<base::DictionaryValue>();
+  node_details->SetString(
+      "label", l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE));
+  node_details->Set("children", std::move(cert_fields));
+  cert_fields = base::MakeUnique<base::ListValue>();
+  cert_fields->Append(std::move(node_details));
+
+  // Top level information.
+  base::ListValue root_list;
+  node_details = base::MakeUnique<base::DictionaryValue>();
+  node_details->SetString("label", x509_certificate_model::GetTitle(cert));
+  node_details->Set("children", std::move(cert_fields));
+  root_list.Append(std::move(node_details));
 
   // Send certificate information to javascript.
   web_ui()->CallJavascriptFunctionUnsafe("cert_viewer.getCertificateFields",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 3e4e6c0d..53e6ac0 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -183,6 +183,10 @@
 #include "chrome/browser/ui/webui/welcome_win10_ui.h"
 #endif
 
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/sandbox_internals_ui.h"
+#endif
+
 #if (defined(USE_NSS_CERTS) || defined(USE_OPENSSL_CERTS)) && defined(USE_AURA)
 #include "chrome/browser/ui/webui/certificate_viewer_ui.h"
 #endif
@@ -293,8 +297,7 @@
           || url.host_piece() == chrome::kChromeUITermsHost
 #endif
 #if defined(OS_LINUX) || defined(OS_OPENBSD)
-          || url.host_piece() == chrome::kChromeUILinuxProxyConfigHost ||
-          url.host_piece() == chrome::kChromeUISandboxHost
+          || url.host_piece() == chrome::kChromeUILinuxProxyConfigHost
 #endif
 #if defined(OS_CHROMEOS)
           || url.host_piece() == chrome::kChromeUIOSCreditsHost
@@ -635,6 +638,11 @@
   }
 #endif
 #endif
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  if (url.host_piece() == chrome::kChromeUISandboxHost) {
+    return &NewWebUI<SandboxInternalsUI>;
+  }
+#endif
   if (IsAboutUI(url))
     return &NewWebUI<AboutUI>;
 
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
index bd117e4..c38c67e 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -426,7 +426,7 @@
       nullptr;
   for (const auto& val : *sources) {
     const base::DictionaryValue* dict;
-    CHECK(val->GetAsDictionary(&dict));
+    CHECK(val.GetAsDictionary(&dict));
     power_manager::PowerSupplyProperties_PowerSource* source =
         props.add_available_external_power_source();
     std::string id;
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
index ded2b60..480c624def 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
@@ -129,27 +129,21 @@
     apps_list.Append(std::move(app_info));
   }
 
-  ArcKioskAppManager::Apps arc_apps;
-  ArcKioskAppManager::Get()->GetAllApps(&arc_apps);
+  const auto& arc_apps = ArcKioskAppManager::Get()->GetAllApps();
   for (size_t i = 0; i < arc_apps.size(); ++i) {
     std::unique_ptr<base::DictionaryValue> app_info(
         new base::DictionaryValue());
     app_info->SetBoolean("isApp", true);
     app_info->SetBoolean("isAndroidApp", true);
-    app_info->SetString("id", arc_apps[i]->app_id());
+    app_info->SetString("id", arc_apps[i].app_info().package_name());
     app_info->SetString("account_email",
-                        arc_apps[i]->account_id().GetUserEmail());
-    app_info->SetString("label", arc_apps[i]->name());
+                        arc_apps[i].account_id().GetUserEmail());
+    app_info->SetString("label", arc_apps[i].name());
 
-    std::string icon_url;
-    if (arc_apps[i]->icon().isNull()) {
-      icon_url =
-          webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
-                                       .GetImageNamed(IDR_APP_DEFAULT_ICON)
-                                       .ToSkBitmap());
-    } else {
-      icon_url = webui::GetBitmapDataUrl(*arc_apps[i]->icon().bitmap());
-    }
+    std::string icon_url =
+        webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
+                                     .GetImageNamed(IDR_APP_DEFAULT_ICON)
+                                     .ToSkBitmap());
     app_info->SetString("iconUrl", icon_url);
 
     apps_list.Append(std::move(app_info));
diff --git a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
index dbc4ab3..c4157b30 100644
--- a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
+++ b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
@@ -477,9 +477,9 @@
     for (base::ListValue::const_iterator available_it =
              available_locales.begin();
          available_it != available_locales.end(); ++available_it) {
-      base::DictionaryValue* dict;
+      const base::DictionaryValue* dict;
       std::string available_locale;
-      if (!(*available_it)->GetAsDictionary(&dict) ||
+      if (!available_it->GetAsDictionary(&dict) ||
           !dict->GetString("value", &available_locale)) {
         NOTREACHED();
         continue;
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index fa32d34..bcb26d6 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -247,7 +247,7 @@
 
   std::string input_method;
   for (const auto& input_method_entry : *login_screen_input_methods) {
-    if (input_method_entry->GetAsString(&input_method))
+    if (input_method_entry.GetAsString(&input_method))
       allowed_input_methods.push_back(input_method);
   }
   chromeos::input_method::InputMethodManager* imm =
diff --git a/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc b/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
index 4a0f275..8e026382 100644
--- a/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
@@ -46,7 +46,7 @@
   const base::ListValue* list;
   if (value.GetAsList(&list)) {
     for (const auto& list_item : *list)
-      ids.push_back(GetId(*list_item));
+      ids.push_back(GetId(list_item));
   } else {
     ids.push_back(GetId(value));
   }
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
index cce634b..e4c42f4 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -88,13 +88,12 @@
       : collator_(collator) {
   }
 
-  bool operator()(const std::unique_ptr<base::Value>& a,
-                  const std::unique_ptr<base::Value>& b) const {
+  bool operator()(const base::Value& a, const base::Value& b) const {
     const base::DictionaryValue* a_dict;
-    bool a_is_dictionary = a->GetAsDictionary(&a_dict);
+    bool a_is_dictionary = a.GetAsDictionary(&a_dict);
     DCHECK(a_is_dictionary);
     const base::DictionaryValue* b_dict;
-    bool b_is_dictionary = b->GetAsDictionary(&b_dict);
+    bool b_is_dictionary = b.GetAsDictionary(&b_dict);
     DCHECK(b_is_dictionary);
     base::string16 a_str;
     base::string16 b_str;
diff --git a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
index 5f5e031..2dfe3d2 100644
--- a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
@@ -98,7 +98,7 @@
   for (base::ListValue::const_iterator i = list_value->begin();
        i != list_value->end(); ++i) {
     std::string email;
-    if ((*i)->GetAsString(&email)) {
+    if (i->GetAsString(&email)) {
       // Translate email to the display email.
       const std::string display_email =
           user_manager->GetUserDisplayEmail(AccountId::FromUserEmail(email));
diff --git a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
index f6348dd..4ea500a5 100644
--- a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
@@ -434,7 +434,7 @@
   builder.ClearPlacements();
   for (const auto& layout : *layouts) {
     const base::DictionaryValue* dictionary;
-    if (!layout->GetAsDictionary(&dictionary)) {
+    if (!layout.GetAsDictionary(&dictionary)) {
       LOG(ERROR) << "Invalid layout dictionary: " << *dictionary;
       continue;
     }
diff --git a/chrome/browser/ui/webui/options/content_settings_handler.cc b/chrome/browser/ui/webui/options/content_settings_handler.cc
index 757ec24..b72dfe7 100644
--- a/chrome/browser/ui/webui/options/content_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/content_settings_handler.cc
@@ -941,8 +941,8 @@
   settings.exceptions.clear();
   for (base::ListValue::const_iterator entry = exceptions.begin();
        entry != exceptions.end(); ++entry) {
-    base::DictionaryValue* dict = nullptr;
-    bool valid_dict = (*entry)->GetAsDictionary(&dict);
+    const base::DictionaryValue* dict = nullptr;
+    bool valid_dict = entry->GetAsDictionary(&dict);
     DCHECK(valid_dict);
 
     std::string origin;
diff --git a/chrome/browser/ui/webui/options/language_options_handler_common.cc b/chrome/browser/ui/webui/options/language_options_handler_common.cc
index 820834e..151d4d7 100644
--- a/chrome/browser/ui/webui/options/language_options_handler_common.cc
+++ b/chrome/browser/ui/webui/options/language_options_handler_common.cc
@@ -266,7 +266,7 @@
   for (base::ListValue::const_iterator it = language_list->begin();
        it != language_list->end(); ++it) {
     std::string lang;
-    (*it)->GetAsString(&lang);
+    it->GetAsString(&lang);
     languages.push_back(lang);
   }
 
diff --git a/chrome/browser/ui/webui/sandbox_internals_ui.cc b/chrome/browser/ui/webui/sandbox_internals_ui.cc
new file mode 100644
index 0000000..63a62253
--- /dev/null
+++ b/chrome/browser/ui/webui/sandbox_internals_ui.cc
@@ -0,0 +1,83 @@
+// 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/sandbox_internals_ui.h"
+
+#include <string>
+#include <unordered_set>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+#if defined(OS_LINUX)
+#include "content/public/browser/zygote_host_linux.h"
+#include "content/public/common/sandbox_linux.h"
+#endif
+
+namespace {
+
+#if defined(OS_LINUX)
+static void SetSandboxStatusData(content::WebUIDataSource* source) {
+  // Get expected sandboxing status of renderers.
+  const int status =
+      content::ZygoteHost::GetInstance()->GetRendererSandboxStatus();
+
+  source->AddBoolean("suid", status & content::kSandboxLinuxSUID);
+  source->AddBoolean("userNs", status & content::kSandboxLinuxUserNS);
+  source->AddBoolean("pidNs", status & content::kSandboxLinuxPIDNS);
+  source->AddBoolean("netNs", status & content::kSandboxLinuxNetNS);
+  source->AddBoolean("seccompBpf", status & content::kSandboxLinuxSeccompBPF);
+  source->AddBoolean("seccompTsync",
+                     status & content::kSandboxLinuxSeccompTSYNC);
+  source->AddBoolean("yama", status & content::kSandboxLinuxYama);
+
+  // Require either the setuid or namespace sandbox for our first-layer sandbox.
+  bool good_layer1 = (status & content::kSandboxLinuxSUID ||
+                      status & content::kSandboxLinuxUserNS) &&
+                     status & content::kSandboxLinuxPIDNS &&
+                     status & content::kSandboxLinuxNetNS;
+  // A second-layer sandbox is also required to be adequately sandboxed.
+  bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
+  source->AddBoolean("sandboxGood", good_layer1 && good_layer2);
+}
+#endif
+
+content::WebUIDataSource* CreateDataSource() {
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUISandboxHost);
+  source->SetDefaultResource(IDR_SANDBOX_INTERNALS_HTML);
+  source->AddResourcePath("sandbox_internals.js", IDR_SANDBOX_INTERNALS_JS);
+  source->UseGzip(std::unordered_set<std::string>());
+
+#if defined(OS_LINUX)
+  SetSandboxStatusData(source);
+  source->SetJsonPath("strings.js");
+#endif
+
+  return source;
+}
+
+}  // namespace
+
+SandboxInternalsUI::SandboxInternalsUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  Profile* profile = Profile::FromWebUI(web_ui);
+  content::WebUIDataSource::Add(profile, CreateDataSource());
+}
+
+void SandboxInternalsUI::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+#if defined(OS_ANDROID)
+  render_frame_host->Send(new ChromeViewMsg_AddSandboxStatusExtension(
+      render_frame_host->GetRoutingID()));
+#endif
+}
+
+SandboxInternalsUI::~SandboxInternalsUI() {}
diff --git a/chrome/browser/ui/webui/sandbox_internals_ui.h b/chrome/browser/ui/webui/sandbox_internals_ui.h
new file mode 100644
index 0000000..c8c59692
--- /dev/null
+++ b/chrome/browser/ui/webui/sandbox_internals_ui.h
@@ -0,0 +1,31 @@
+// 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_SANDBOX_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SANDBOX_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// This WebUI page displays the status of the renderer sandbox on Linux and
+// Android. The two OSes share the same basic page, but the data reported are
+// obtained from different places:
+//    - On Linux, this object in the browser queries the renderer ZygoteHost
+//      to get the sandbox status of the renderers. The data are then specified
+//      as loadTimeData on the WebUI Page.
+//    - On Android, this object sends an IPC message to the
+//      SandboxStatusExtension in the renderer, which installs a JavaScript
+//      function on the web page to return the current sandbox status.
+class SandboxInternalsUI : public content::WebUIController {
+ public:
+  explicit SandboxInternalsUI(content::WebUI* web_ui);
+  ~SandboxInternalsUI() override;
+
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SandboxInternalsUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SANDBOX_INTERNALS_UI_H_
diff --git a/chrome/browser/ui/webui/settings/certificates_handler.cc b/chrome/browser/ui/webui/settings/certificates_handler.cc
index 9930c7b..d80b14b 100644
--- a/chrome/browser/ui/webui/settings/certificates_handler.cc
+++ b/chrome/browser/ui/webui/settings/certificates_handler.cc
@@ -85,15 +85,14 @@
   explicit DictionaryIdComparator(icu::Collator* collator)
       : collator_(collator) {}
 
-  bool operator()(const std::unique_ptr<base::Value>& a,
-                  const std::unique_ptr<base::Value>& b) const {
-    DCHECK(a->GetType() == base::Value::Type::DICTIONARY);
-    DCHECK(b->GetType() == base::Value::Type::DICTIONARY);
+  bool operator()(const base::Value& a, const base::Value& b) const {
+    DCHECK(a.GetType() == base::Value::Type::DICTIONARY);
+    DCHECK(b.GetType() == base::Value::Type::DICTIONARY);
     const base::DictionaryValue* a_dict;
-    bool a_is_dictionary = a->GetAsDictionary(&a_dict);
+    bool a_is_dictionary = a.GetAsDictionary(&a_dict);
     DCHECK(a_is_dictionary);
     const base::DictionaryValue* b_dict;
-    bool b_is_dictionary = b->GetAsDictionary(&b_dict);
+    bool b_is_dictionary = b.GetAsDictionary(&b_dict);
     DCHECK(b_is_dictionary);
     base::string16 a_str;
     base::string16 b_str;
diff --git a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
index 5cc79c1..8c02f06 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -27,7 +27,8 @@
   auto response = base::MakeUnique<base::DictionaryValue>();
   auto fingerprints = base::MakeUnique<base::ListValue>();
 
-  DCHECK(int{fingerprints_list.size()} <= kMaxAllowedFingerprints);
+  DCHECK_LE(static_cast<int>(fingerprints_list.size()),
+            kMaxAllowedFingerprints);
   for (auto& fingerprint_name: fingerprints_list) {
     std::unique_ptr<base::Value> str =
         base::MakeUnique<base::Value>(fingerprint_name);
@@ -35,8 +36,8 @@
   }
 
   response->Set("fingerprintsList", std::move(fingerprints));
-  response->SetBoolean(
-      "isMaxed", int{fingerprints_list.size()} >= kMaxAllowedFingerprints);
+  response->SetBoolean("isMaxed", static_cast<int>(fingerprints_list.size()) >=
+                                      kMaxAllowedFingerprints);
   return response;
 }
 
@@ -113,8 +114,9 @@
   std::string callback_id;
   CHECK(args->GetString(0, &callback_id));
 
-  ResolveJavascriptCallback(base::Value(callback_id),
-                            base::Value(int{fingerprints_list_.size()}));
+  ResolveJavascriptCallback(
+      base::Value(callback_id),
+      base::Value(static_cast<int>(fingerprints_list_.size())));
 }
 
 void FingerprintHandler::HandleStartEnroll(const base::ListValue* args) {
@@ -133,7 +135,7 @@
   CHECK(args->GetString(0, &callback_id));
   CHECK(args->GetInteger(1, &index));
 
-  DCHECK(index < int{fingerprints_list_.size()});
+  DCHECK_LT(index, static_cast<int>(fingerprints_list_.size()));
   ResolveJavascriptCallback(base::Value(callback_id),
                             base::Value(fingerprints_list_[index]));
 }
@@ -147,7 +149,7 @@
   CHECK(args->GetString(0, &callback_id));
   CHECK(args->GetInteger(1, &index));
 
-  DCHECK(index < int{fingerprints_list_.size()});
+  DCHECK_LT(index, static_cast<int>(fingerprints_list_.size()));
   bool deleteSucessful = true;
   fingerprints_list_.erase(fingerprints_list_.begin() + index);
   ResolveJavascriptCallback(base::Value(callback_id),
@@ -175,7 +177,8 @@
 
 void FingerprintHandler::HandleFakeScanComplete(
     const base::ListValue* args) {
-  DCHECK(int{fingerprints_list_.size()} < kMaxAllowedFingerprints);
+  DCHECK_LT(static_cast<int>(fingerprints_list_.size()),
+            kMaxAllowedFingerprints);
   // Determines what the newly added fingerprint's name should be.
   for (int i = 1; i <= kMaxAllowedFingerprints; ++i) {
     std::string fingerprint_name = l10n_util::GetStringFUTF8(
@@ -204,7 +207,8 @@
     auto label_it = std::find(fingerprints_list_.begin(),
                               fingerprints_list_.end(), matched_label);
     DCHECK(label_it != fingerprints_list_.end());
-    user_ids->AppendInteger(int{label_it - fingerprints_list_.begin()});
+    user_ids->AppendInteger(
+        static_cast<int>(label_it - fingerprints_list_.begin()));
   }
 
   fingerprint_attempt->SetInteger("result", result);
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index 70ef6fa8..8c28fbe 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -307,7 +307,7 @@
   std::vector<ContentSettingsPattern> patterns;
   for (const auto& entry : *policy_urls) {
     std::string url;
-    bool valid_string = entry->GetAsString(&url);
+    bool valid_string = entry.GetAsString(&url);
     if (!valid_string)
       continue;
 
diff --git a/chrome/common/extensions/api/common_extension_api_unittest.cc b/chrome/common/extensions/api/common_extension_api_unittest.cc
index 776db1c9..3d6e2c6 100644
--- a/chrome/common/extensions/api/common_extension_api_unittest.cc
+++ b/chrome/common/extensions/api/common_extension_api_unittest.cc
@@ -802,7 +802,7 @@
     const base::DictionaryValue* ret = nullptr;
     for (const auto& val : *list) {
       const base::DictionaryValue* dict = nullptr;
-      if (!val->GetAsDictionary(&dict))
+      if (!val.GetAsDictionary(&dict))
         continue;
       std::string str;
       if (dict->GetString(key, &str) && str == value) {
diff --git a/chrome/common/extensions/api/extension_action/action_info.cc b/chrome/common/extensions/api/extension_action/action_info.cc
index aa8376b0..c30141df 100644
--- a/chrome/common/extensions/api/extension_action/action_info.cc
+++ b/chrome/common/extensions/api/extension_action/action_info.cc
@@ -68,8 +68,7 @@
         dict->GetList(keys::kPageActionIcons, &icons)) {
       base::ListValue::const_iterator iter = icons->begin();
       std::string path;
-      if (iter == icons->end() ||
-          !(*iter)->GetAsString(&path) ||
+      if (iter == icons->end() || !iter->GetAsString(&path) ||
           !manifest_handler_helpers::NormalizeAndValidatePath(&path)) {
         *error = base::ASCIIToUTF16(errors::kInvalidPageActionIconPath);
         return std::unique_ptr<ActionInfo>();
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
index 44a4ff1..b07e89c 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
@@ -254,8 +254,8 @@
     FileBrowserHandler::List* result,
     base::string16* error) {
   for (const auto& entry : *extension_actions) {
-    base::DictionaryValue* dict;
-    if (!entry->GetAsDictionary(&dict)) {
+    const base::DictionaryValue* dict;
+    if (!entry.GetAsDictionary(&dict)) {
       *error = base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
       return false;
     }
diff --git a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
index 61dc4998..4f971b9 100644
--- a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
+++ b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
@@ -114,7 +114,7 @@
   for (base::ListValue::const_iterator it = manif_patterns->begin();
        it != manif_patterns->end(); ++it) {
     std::string str_pattern;
-    (*it)->GetAsString(&str_pattern);
+    it->GetAsString(&str_pattern);
     // TODO(sergeygs): Limit this to non-top-level domains.
     // TODO(sergeygs): Also add a verification to the CWS installer that the
     // URL patterns claimed here belong to the app's author verified sites.
diff --git a/chrome/common/extensions/manifest_handlers/linked_app_icons.cc b/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
index 744a2d16..d8e2fac 100644
--- a/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
+++ b/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
@@ -70,7 +70,7 @@
 
     for (const auto& icon_value : *icons_list) {
       const base::DictionaryValue* icon_dict = nullptr;
-      if (!icon_value->GetAsDictionary(&icon_dict)) {
+      if (!icon_value.GetAsDictionary(&icon_dict)) {
         *error = base::UTF8ToUTF16(
             extensions::manifest_errors::kInvalidLinkedAppIcon);
         return false;
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 502771b..61cc03b8 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -190,6 +190,9 @@
 // is being shown in error page.
 IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_SetIsShowingDownloadButtonInErrorPage,
                     bool /* showing download button */)
+
+// Sent when navigating to chrome://sandbox to install bindings onto the WebUI.
+IPC_MESSAGE_ROUTED0(ChromeViewMsg_AddSandboxStatusExtension)
 #endif  // defined(OS_ANDROID)
 
 //-----------------------------------------------------------------------------
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 1e790dfc..d854657 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -281,6 +281,9 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 const char kChromeUILinuxProxyConfigHost[] = "linux-proxy-config";
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
 const char kChromeUISandboxHost[] = "sandbox";
 #endif
 
@@ -727,6 +730,8 @@
 #endif
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
     kChromeUILinuxProxyConfigHost,
+#endif
+#if defined(OS_LINUX) || defined(OS_ANDROID)
     kChromeUISandboxHost,
 #endif
 #if defined(OS_WIN)
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index efa2f84..bc4762b 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -262,6 +262,9 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 extern const char kChromeUILinuxProxyConfigHost[];
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
 extern const char kChromeUISandboxHost[];
 #endif
 
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 6c726e4..4fb9f9d 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -71,6 +71,8 @@
     "prerender/prerender_helper.h",
     "prerender/prerenderer_client.cc",
     "prerender/prerenderer_client.h",
+    "sandbox_status_extension_android.cc",
+    "sandbox_status_extension_android.h",
     "searchbox/search_bouncer.cc",
     "searchbox/search_bouncer.h",
     "searchbox/searchbox.cc",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index dab69468..0c4f7b2 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -124,6 +124,10 @@
 #include "ui/base/webui/jstemplate_builder.h"
 #include "url/origin.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/renderer/sandbox_status_extension_android.h"
+#endif
+
 #if !defined(DISABLE_NACL)
 #include "components/nacl/common/nacl_constants.h"
 #include "components/nacl/renderer/nacl_helper.h"
@@ -520,6 +524,10 @@
       render_frame, base::MakeUnique<ChromePrintWebViewHelperDelegate>());
 #endif
 
+#if defined(OS_ANDROID)
+  SandboxStatusExtension::Create(render_frame);
+#endif
+
   new NetErrorHelper(render_frame);
 
   new page_load_metrics::MetricsRenderFrameObserver(render_frame);
diff --git a/chrome/renderer/sandbox_status_extension_android.cc b/chrome/renderer/sandbox_status_extension_android.cc
new file mode 100644
index 0000000..bfaff31
--- /dev/null
+++ b/chrome/renderer/sandbox_status_extension_android.cc
@@ -0,0 +1,156 @@
+// 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/renderer/sandbox_status_extension_android.h"
+
+#include "base/android/build_info.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/child/v8_value_converter.h"
+#include "content/public/renderer/chrome_object_extensions_utils.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/seccomp_sandbox_status_android.h"
+#include "gin/arguments.h"
+#include "gin/function_template.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/web/WebDataSource.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "v8/include/v8.h"
+
+SandboxStatusExtension::SandboxStatusExtension(content::RenderFrame* frame)
+    : content::RenderFrameObserver(frame) {}
+
+SandboxStatusExtension::~SandboxStatusExtension() {}
+
+// static
+void SandboxStatusExtension::Create(content::RenderFrame* frame) {
+  auto* extension = new SandboxStatusExtension(frame);
+  extension->AddRef();  // Balanced in OnDestruct().
+}
+
+void SandboxStatusExtension::OnDestruct() {
+  // This object is ref-counted, since a callback could still be in-flight.
+  Release();
+}
+
+bool SandboxStatusExtension::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+
+  IPC_BEGIN_MESSAGE_MAP(SandboxStatusExtension, message)
+    IPC_MESSAGE_HANDLER(ChromeViewMsg_AddSandboxStatusExtension,
+                        OnAddSandboxStatusExtension)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+
+void SandboxStatusExtension::DidClearWindowObject() {
+  Install();
+}
+
+void SandboxStatusExtension::OnAddSandboxStatusExtension() {
+  should_install_ = true;
+}
+
+void SandboxStatusExtension::Install() {
+  if (!should_install_)
+    return;
+
+  v8::Isolate* isolate = blink::MainThreadIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context =
+      render_frame()->GetWebFrame()->MainWorldScriptContext();
+  if (context.IsEmpty())
+    return;
+
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::Object> chrome =
+      content::GetOrCreateChromeObject(isolate, context->Global());
+  DCHECK(chrome->Set(
+      gin::StringToSymbol(isolate, "getAndroidSandboxStatus"),
+      gin::CreateFunctionTemplate(
+          isolate, base::Bind(&SandboxStatusExtension::GetSandboxStatus, this))
+          ->GetFunction()));
+}
+
+void SandboxStatusExtension::GetSandboxStatus(gin::Arguments* args) {
+  if (!render_frame())
+    return;
+
+  if (render_frame()->GetWebFrame()->GetSecurityOrigin().Host() !=
+      chrome::kChromeUISandboxHost) {
+    args->ThrowTypeError("Not allowed on this origin");
+    return;
+  }
+
+  v8::HandleScope handle_scope(args->isolate());
+
+  v8::Local<v8::Function> callback;
+  if (!args->GetNext(&callback)) {
+    args->ThrowError();
+    return;
+  }
+
+  auto global_callback =
+      base::MakeUnique<v8::Global<v8::Function>>(args->isolate(), callback);
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, base::TaskTraits().MayBlock(),
+      base::Bind(&SandboxStatusExtension::ReadSandboxStatus, this),
+      base::Bind(&SandboxStatusExtension::RunCallback, this,
+                 base::Passed(&global_callback)));
+}
+
+std::unique_ptr<base::Value> SandboxStatusExtension::ReadSandboxStatus() {
+  std::string secontext;
+  base::FilePath path(FILE_PATH_LITERAL("/proc/self/attr/current"));
+  base::ReadFileToString(path, &secontext);
+
+  std::string proc_status;
+  path = base::FilePath(FILE_PATH_LITERAL("/proc/self/status"));
+  base::ReadFileToString(path, &proc_status);
+
+  auto status = base::MakeUnique<base::DictionaryValue>();
+  status->SetInteger("uid", getuid());
+  status->SetInteger("pid", getpid());
+  status->SetString("secontext", secontext);
+  status->SetInteger("seccompStatus",
+                     static_cast<int>(content::GetSeccompSandboxStatus()));
+  status->SetString("procStatus", proc_status);
+  status->SetString(
+      "androidBuildId",
+      base::android::BuildInfo::GetInstance()->android_build_id());
+
+  return std::move(status);
+}
+
+void SandboxStatusExtension::RunCallback(
+    std::unique_ptr<v8::Global<v8::Function>> callback,
+    std::unique_ptr<base::Value> status) {
+  if (!render_frame())
+    return;
+
+  v8::Isolate* isolate = blink::MainThreadIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context =
+      render_frame()->GetWebFrame()->MainWorldScriptContext();
+  v8::Context::Scope context_scope(context);
+  v8::Local<v8::Function> callback_local =
+      v8::Local<v8::Function>::New(isolate, *callback);
+
+  std::unique_ptr<content::V8ValueConverter> converter(
+      content::V8ValueConverter::create());
+
+  v8::Local<v8::Value> argv[] = {converter->ToV8Value(status.get(), context)};
+  render_frame()->GetWebFrame()->CallFunctionEvenIfScriptDisabled(
+      callback_local, v8::Object::New(isolate), 1, argv);
+}
diff --git a/chrome/renderer/sandbox_status_extension_android.h b/chrome/renderer/sandbox_status_extension_android.h
new file mode 100644
index 0000000..8b4a547
--- /dev/null
+++ b/chrome/renderer/sandbox_status_extension_android.h
@@ -0,0 +1,68 @@
+// 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_RENDERER_SANDBOX_STATUS_EXTENSION_ANDROID_H_
+#define CHROME_RENDERER_SANDBOX_STATUS_EXTENSION_ANDROID_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+class Arguments;
+}
+
+// On Android, this class adds a function chrome.getAndroidSandboxStatus()
+// to the chrome://sandbox/ WebUI page. This is done only after the browser
+// SandboxInternalsUI sends an IPC mesage blessing this RenderFrame.
+class SandboxStatusExtension
+    : public base::RefCountedThreadSafe<SandboxStatusExtension>,
+      public content::RenderFrameObserver {
+ public:
+  // Creates a new SandboxStatusExtension for the |frame|.
+  static void Create(content::RenderFrame* frame);
+
+  // content::RenderFrameObserver:
+  void OnDestruct() override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+  void DidClearWindowObject() override;
+
+ protected:
+  friend class RefCountedThreadSafe<SandboxStatusExtension>;
+  ~SandboxStatusExtension() override;
+
+ private:
+  explicit SandboxStatusExtension(content::RenderFrame* frame);
+
+  // IPC message handler.
+  void OnAddSandboxStatusExtension();
+
+  // Installs the JavaScript function into the scripting context, if
+  // should_install_ is true.
+  void Install();
+
+  // Native implementation of chrome.getAndroidSandboxStatus.
+  void GetSandboxStatus(gin::Arguments* args);
+
+  // Called on the blocking pool, this gets the sandbox status of the current
+  // renderer process and returns a status object as a base::Value.
+  std::unique_ptr<base::Value> ReadSandboxStatus();
+
+  // Runs the callback argument provided to GetSandboxStatus() with the status
+  // object computed by ReadSandboxStatus(). This is called back on the thread
+  // on which GetSandboxStatus() was called originally.
+  void RunCallback(std::unique_ptr<v8::Global<v8::Function>> callback,
+                   std::unique_ptr<base::Value> status);
+
+  // Set to true by OnAddSandboxStatusExtension().
+  bool should_install_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(SandboxStatusExtension);
+};
+
+#endif  // CHROME_RENDERER_SANDBOX_STATUS_EXTENSION_ANDROID_H_
diff --git a/chrome/service/cloud_print/printer_job_queue_handler.cc b/chrome/service/cloud_print/printer_job_queue_handler.cc
index 4757bd9..64aa593b 100644
--- a/chrome/service/cloud_print/printer_job_queue_handler.cc
+++ b/chrome/service/cloud_print/printer_job_queue_handler.cc
@@ -120,7 +120,7 @@
   std::vector<JobDetails> jobs_with_timeouts;
   for (const auto& job_value : *job_list) {
     const base::DictionaryValue* job_data = nullptr;
-    if (!job_value->GetAsDictionary(&job_data))
+    if (!job_value.GetAsDictionary(&job_data))
       continue;
 
     JobDetails job_details_current = ConstructJobDetailsFromJson(*job_data);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4a0ff237c..b309509a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3064,13 +3064,11 @@
     sources += [
       "../browser/resources/chromeos/braille_ime/braille_ime_unittest.gtestjs",
       "../browser/resources/chromeos/select_to_speak/select_to_speak_unittest.gtestjs",
-      "../browser/resources/chromeos/switch_access/tree_walker_unittest.gtestjs",
     ]
     extra_js_files += [
       "../browser/resources/chromeos/braille_ime/braille_ime.js",
       "../browser/resources/chromeos/select_to_speak/select_to_speak.js",
       "../browser/resources/chromeos/select_to_speak/test_support.js",
-      "../browser/resources/chromeos/switch_access/tree_walker.js",
     ]
   }
 }
@@ -4419,6 +4417,7 @@
 
   if (enable_media_router) {
     sources += [
+      "../browser/media/android/router/media_router_android_unittest.cc",
       "../browser/media/cast_remoting_connector_unittest.cc",
       "../browser/media/router/browser_presentation_connection_proxy_unittest.cc",
       "../browser/media/router/create_presentation_connection_request_unittest.cc",
@@ -4834,6 +4833,7 @@
     sources += [
       "../browser/media/webrtc/native_desktop_media_list_unittest.cc",
       "../browser/metrics/desktop_session_duration/desktop_session_duration_tracker_unittest.cc",
+      "../browser/signin/force_signin_verifier_unittest.cc",
       "../browser/signin/signin_global_error_unittest.cc",
       "../browser/signin/signin_util_unittest.cc",
       "../browser/ui/webui/signin/signin_create_profile_handler_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
index bd9b9b7..44a8685 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
@@ -6,6 +6,7 @@
 
 import android.app.Notification;
 
+import org.chromium.chrome.browser.notifications.ChannelsInitializer;
 import org.chromium.chrome.browser.notifications.NotificationManagerProxy;
 
 import java.util.ArrayList;
@@ -21,6 +22,8 @@
  */
 public class MockNotificationManagerProxy implements NotificationManagerProxy {
     private static final String KEY_SEPARATOR = ":";
+    private List<ChannelsInitializer.Channel> mChannels;
+    private List<ChannelsInitializer.ChannelGroup> mNotificationChannelGroups;
 
     /**
      * Holds a notification and the arguments passed to #notify and #cancel.
@@ -45,6 +48,8 @@
     public MockNotificationManagerProxy() {
         mNotifications = new LinkedHashMap<>();
         mMutationCount = 0;
+        mChannels = new ArrayList<>();
+        mNotificationChannelGroups = new ArrayList<>();
     }
 
     /**
@@ -91,6 +96,24 @@
     }
 
     @Override
+    public void createNotificationChannel(ChannelsInitializer.Channel channel) {
+        mChannels.add(channel);
+    }
+
+    public List<ChannelsInitializer.Channel> getChannels() {
+        return mChannels;
+    }
+
+    @Override
+    public void createNotificationChannelGroup(ChannelsInitializer.ChannelGroup channelGroup) {
+        mNotificationChannelGroups.add(channelGroup);
+    }
+
+    public List<ChannelsInitializer.ChannelGroup> getNotificationChannelGroups() {
+        return mNotificationChannelGroups;
+    }
+
+    @Override
     public void notify(int id, Notification notification) {
         notify(null /* tag */, id, notification);
     }
diff --git a/chrome/test/chromedriver/chrome/network_conditions.cc b/chrome/test/chromedriver/chrome/network_conditions.cc
index 86166eb..bc28c9e 100644
--- a/chrome/test/chromedriver/chrome/network_conditions.cc
+++ b/chrome/test/chromedriver/chrome/network_conditions.cc
@@ -39,7 +39,7 @@
        it != networks->end();
        ++it) {
     base::DictionaryValue* network = NULL;
-    if (!(*it)->GetAsDictionary(&network)) {
+    if (!it->GetAsDictionary(&network)) {
       return Status(kUnknownError,
                     "malformed network in list: should be a dictionary");
     }
diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc
index 71dfbd56..f99a049 100644
--- a/chrome/test/chromedriver/logging.cc
+++ b/chrome/test/chromedriver/logging.cc
@@ -171,8 +171,8 @@
   for (base::ListValue::const_iterator it = list->begin();
        it != list->end();
        ++it) {
-    base::DictionaryValue* log_entry = NULL;
-    (*it)->GetAsDictionary(&log_entry);
+    const base::DictionaryValue* log_entry = NULL;
+    it->GetAsDictionary(&log_entry);
     if (log_entry != NULL) {
       std::string level;
       if (log_entry->GetString("level", &level))
diff --git a/chrome/test/chromedriver/performance_logger.cc b/chrome/test/chromedriver/performance_logger.cc
index d7f61c54..23d94e9 100644
--- a/chrome/test/chromedriver/performance_logger.cc
+++ b/chrome/test/chromedriver/performance_logger.cc
@@ -190,8 +190,8 @@
                     "received DevTools trace data in unexpected format");
     }
     for (const auto& trace : *traces) {
-      base::DictionaryValue* event_dict;
-      if (!trace->GetAsDictionary(&event_dict))
+      const base::DictionaryValue* event_dict;
+      if (!trace.GetAsDictionary(&event_dict))
         return Status(kUnknownError, "trace event must be a dictionary");
       AddLogEntry(client->GetId(), "Tracing.dataCollected", *event_dict);
     }
diff --git a/chrome/utility/importer/nss_decryptor.cc b/chrome/utility/importer/nss_decryptor.cc
index 1698420..3c45524a 100644
--- a/chrome/utility/importer/nss_decryptor.cc
+++ b/chrome/utility/importer/nss_decryptor.cc
@@ -306,7 +306,7 @@
   if (password_dict->GetList("disabledHosts", &blacklist_domains)) {
     for (const auto& value : *blacklist_domains) {
       std::string disabled_host;
-      if (!value->GetAsString(&disabled_host))
+      if (!value.GetAsString(&disabled_host))
         continue;
       forms->push_back(CreateBlacklistPasswordForm(disabled_host));
     }
@@ -315,7 +315,7 @@
   if (password_dict->GetList("logins", &password_list)) {
     for (const auto& value : *password_list) {
       const base::DictionaryValue* password_detail;
-      if (!value->GetAsDictionary(&password_detail))
+      if (!value.GetAsDictionary(&password_detail))
         continue;
 
       FirefoxRawPasswordInfo raw_password_info;
diff --git a/chromecast/crash/linux/crash_testing_utils.cc b/chromecast/crash/linux/crash_testing_utils.cc
index 4649fc9..6ba362f 100644
--- a/chromecast/crash/linux/crash_testing_utils.cc
+++ b/chromecast/crash/linux/crash_testing_utils.cc
@@ -63,7 +63,7 @@
   std::string lockfile;
 
   for (const auto& elem : *contents) {
-    std::unique_ptr<std::string> dump_info = SerializeToJson(*elem);
+    std::unique_ptr<std::string> dump_info = SerializeToJson(elem);
     RCHECK(dump_info, -1, "Failed to serialize DumpInfo");
     lockfile += *dump_info;
     lockfile += "\n";  // Add line seperatators
@@ -95,7 +95,7 @@
   dumps->clear();
 
   for (const auto& elem : *dump_list) {
-    std::unique_ptr<DumpInfo> dump(new DumpInfo(elem.get()));
+    std::unique_ptr<DumpInfo> dump(new DumpInfo(&elem));
     RCHECK(dump->valid(), false, "Invalid DumpInfo");
     dumps->push_back(std::move(dump));
   }
diff --git a/chromecast/crash/linux/synchronized_minidump_manager.cc b/chromecast/crash/linux/synchronized_minidump_manager.cc
index 4f1b7918..49cb3dd 100644
--- a/chromecast/crash/linux/synchronized_minidump_manager.cc
+++ b/chromecast/crash/linux/synchronized_minidump_manager.cc
@@ -267,7 +267,7 @@
   std::string lockfile;
 
   for (const auto& elem : *dumps) {
-    std::unique_ptr<std::string> dump_info = SerializeToJson(*elem);
+    std::unique_ptr<std::string> dump_info = SerializeToJson(elem);
     RCHECK(dump_info, false);
     lockfile += *dump_info;
     lockfile += "\n";  // Add line seperatators
@@ -332,7 +332,7 @@
   std::vector<std::unique_ptr<DumpInfo>> dumps;
 
   for (const auto& elem : *dumps_) {
-    dumps.push_back(std::unique_ptr<DumpInfo>(new DumpInfo(elem.get())));
+    dumps.push_back(std::unique_ptr<DumpInfo>(new DumpInfo(&elem)));
   }
 
   return dumps;
diff --git a/chromeos/chromeos_paths.cc b/chromeos/chromeos_paths.cc
index bdb3fa9..53177d2 100644
--- a/chromeos/chromeos_paths.cc
+++ b/chromeos/chromeos_paths.cc
@@ -32,6 +32,9 @@
 const base::FilePath::CharType kMachineHardwareInfoFileName[] =
     FILE_PATH_LITERAL("/tmp/machine-info");
 
+const base::FilePath::CharType kVpdFileName[] =
+    FILE_PATH_LITERAL("/var/log/vpd_2.0.txt");
+
 const base::FilePath::CharType kUptimeFileName[] =
     FILE_PATH_LITERAL("/proc/uptime");
 
@@ -73,6 +76,9 @@
     case FILE_MACHINE_INFO:
       *result = base::FilePath(kMachineHardwareInfoFileName);
       break;
+    case FILE_VPD:
+      *result = base::FilePath(kVpdFileName);
+      break;
     case FILE_UPTIME:
       *result = base::FilePath(kUptimeFileName);
       break;
@@ -111,8 +117,8 @@
 
 void RegisterStubPathOverrides(const base::FilePath& stubs_dir) {
   CHECK(!base::SysInfo::IsRunningOnChromeOS());
-  // Override these paths on the desktop, so that enrollment and cloud
-  // policy work and can be tested.
+  // Override these paths on the desktop, so that enrollment and cloud policy
+  // work and can be tested.
   base::FilePath parent = base::MakeAbsoluteFilePath(stubs_dir);
   PathService::Override(
       DIR_USER_POLICY_KEYS,
@@ -134,6 +140,11 @@
       parent.AppendASCII("stub_machine-info"),
       is_absolute,
       create);
+  PathService::OverrideAndCreateIfNeeded(
+      FILE_VPD,
+      parent.AppendASCII("stub_vpd"),
+      is_absolute,
+      create);
   PathService::Override(
       DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
       parent.AppendASCII("stub_device_local_account_extensions"));
diff --git a/chromeos/chromeos_paths.h b/chromeos/chromeos_paths.h
index cac28aa..c35179f 100644
--- a/chromeos/chromeos_paths.h
+++ b/chromeos/chromeos_paths.h
@@ -26,6 +26,7 @@
   FILE_OWNER_KEY,           // Full path to the owner key file.
   FILE_INSTALL_ATTRIBUTES,  // Full path to the install attributes file.
   FILE_MACHINE_INFO,        // Full path to machine hardware info file.
+  FILE_VPD,                 // Full path to VPD file.
   FILE_UPTIME,              // Full path to the file via which the kernel
                             // exposes the current device uptime.
   FILE_UPDATE_REBOOT_NEEDED_UPTIME,  // Full path to a file in which Chrome can
diff --git a/chromeos/components/tether/ble_scanner.h b/chromeos/components/tether/ble_scanner.h
index 3faffc0..6ef2ae2 100644
--- a/chromeos/components/tether/ble_scanner.h
+++ b/chromeos/components/tether/ble_scanner.h
@@ -82,8 +82,6 @@
              const LocalDeviceDataProvider* local_device_data_provider);
 
   void UpdateDiscoveryStatus();
-  void InitializeBluetoothAdapter();
-  void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter);
   void StartDiscoverySession();
   void OnDiscoverySessionStarted(
       std::unique_ptr<device::BluetoothDiscoverySession> discovery_session);
diff --git a/chromeos/dbus/biod/biod_client_unittest.cc b/chromeos/dbus/biod/biod_client_unittest.cc
index b3185cb..8bb2a90 100644
--- a/chromeos/dbus/biod/biod_client_unittest.cc
+++ b/chromeos/dbus/biod/biod_client_unittest.cc
@@ -172,7 +172,7 @@
     dbus::Signal signal(kInterface,
                         biod::kBiometricsManagerEnrollScanDoneSignal);
     dbus::MessageWriter writer(&signal);
-    writer.AppendUint32(uint32_t{scan_result});
+    writer.AppendUint32(static_cast<uint32_t>(scan_result));
     writer.AppendBool(enroll_session_complete);
     EmitSignal(&signal);
   }
@@ -182,7 +182,7 @@
                               const AuthScanMatches& matches) {
     dbus::Signal signal(kInterface, biod::kBiometricsManagerAuthScanDoneSignal);
     dbus::MessageWriter writer(&signal);
-    writer.AppendUint32(uint32_t{scan_result});
+    writer.AppendUint32(static_cast<uint32_t>(scan_result));
 
     dbus::MessageWriter array_writer(nullptr);
     writer.OpenArray("{sx}", &array_writer);
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc
index 0c103e71..9a1029b8 100644
--- a/chromeos/dbus/fake_shill_manager_client.cc
+++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -67,7 +67,7 @@
   for (base::ListValue::const_iterator iter = service_list_in->begin();
        iter != service_list_in->end(); ++iter) {
     std::string service_path;
-    if (!(*iter)->GetAsString(&service_path))
+    if (!iter->GetAsString(&service_path))
       continue;
     const base::DictionaryValue* properties =
         service_client->GetServiceProperties(service_path);
@@ -1027,7 +1027,7 @@
     for (base::ListValue::const_iterator iter = service_list->begin();
          iter != service_list->end(); ++iter) {
       std::string service_path;
-      if (!(*iter)->GetAsString(&service_path))
+      if (!iter->GetAsString(&service_path))
         continue;
       const base::DictionaryValue* properties =
           service_client->GetServiceProperties(service_path);
@@ -1038,7 +1038,7 @@
       std::string type;
       properties->GetString(shill::kTypeProperty, &type);
       if (TechnologyEnabled(type))
-        new_service_list->Append((*iter)->CreateDeepCopy());
+        new_service_list->Append(iter->CreateDeepCopy());
     }
   }
   return new_service_list;
diff --git a/chromeos/dbus/session_manager_client.cc b/chromeos/dbus/session_manager_client.cc
index 9e87be8..90322ff 100644
--- a/chromeos/dbus/session_manager_client.cc
+++ b/chromeos/dbus/session_manager_client.cc
@@ -42,6 +42,7 @@
 
 constexpr char kStubPolicyFile[] = "stub_policy";
 constexpr char kStubDevicePolicyFile[] = "stub_device_policy";
+constexpr char kStubStateKeysFile[] = "stub_state_keys";
 
 // Returns a location for |file| that is specific to the given |cryptohome_id|.
 // These paths will be relative to DIR_USER_POLICY_KEYS, and can be used only
@@ -74,6 +75,34 @@
   }
 }
 
+// Helper to asynchronously read (or if missing create) state key stubs.
+std::vector<std::string> ReadCreateStateKeysStub(const base::FilePath& path) {
+  std::string contents;
+  if (base::PathExists(path)) {
+    contents = GetFileContent(path);
+  } else {
+    // Create stub state keys on the fly.
+    for (int i = 0; i < 5; ++i) {
+      contents += crypto::SHA256HashString(
+          base::IntToString(i) +
+          base::Int64ToString(base::Time::Now().ToJavaTime()));
+    }
+    StoreFile(path, contents);
+  }
+
+  std::vector<std::string> state_keys;
+  for (size_t i = 0; i < contents.size() / 32; ++i) {
+    state_keys.push_back(contents.substr(i * 32, 32));
+  }
+  return state_keys;
+}
+
+// Turn pass-by-value into pass-by-reference as expected by StateKeysCallback.
+void RunStateKeysCallbackStub(SessionManagerClient::StateKeysCallback callback,
+                              std::vector<std::string> state_keys) {
+  callback.Run(state_keys);
+}
+
 }  // namespace
 
 // The SessionManagerClient implementation used in production.
@@ -991,12 +1020,18 @@
                        const std::vector<std::string>& flags) override {}
 
   void GetServerBackedStateKeys(const StateKeysCallback& callback) override {
-    std::vector<std::string> state_keys;
-    for (int i = 0; i < 5; ++i)
-      state_keys.push_back(crypto::SHA256HashString(base::IntToString(i)));
-
-    if (!callback.is_null())
-      callback.Run(state_keys);
+    base::FilePath owner_key_path;
+    CHECK(PathService::Get(chromeos::FILE_OWNER_KEY, &owner_key_path));
+    const base::FilePath state_keys_path =
+        owner_key_path.DirName().AppendASCII(kStubStateKeysFile);
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE,
+        base::TaskTraits()
+            .WithShutdownBehavior(
+                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+            .MayBlock(),
+        base::Bind(&ReadCreateStateKeysStub, state_keys_path),
+        base::Bind(&RunStateKeysCallbackStub, callback));
   }
 
   void CheckArcAvailability(const ArcCallback& callback) override {
diff --git a/chromeos/dbus/session_manager_client.h b/chromeos/dbus/session_manager_client.h
index b294b761..042c4cc 100644
--- a/chromeos/dbus/session_manager_client.h
+++ b/chromeos/dbus/session_manager_client.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "base/time/time.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
diff --git a/chromeos/dbus/shill_client_helper.cc b/chromeos/dbus/shill_client_helper.cc
index 46ba9b5..fbff208 100644
--- a/chromeos/dbus/shill_client_helper.cc
+++ b/chromeos/dbus/shill_client_helper.cc
@@ -465,7 +465,7 @@
       variant_writer.OpenArray("s", &array_writer);
       for (base::ListValue::const_iterator it = list->begin();
            it != list->end(); ++it) {
-        const base::Value& value = **it;
+        const base::Value& value = *it;
         std::string value_string;
         if (!value.GetAsString(&value_string))
           NET_LOG(ERROR) << "List value not a string: " << value;
diff --git a/chromeos/dbus/shill_ipconfig_client.cc b/chromeos/dbus/shill_ipconfig_client.cc
index 84c1e1a..c58c0c0 100644
--- a/chromeos/dbus/shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill_ipconfig_client.cc
@@ -126,10 +126,10 @@
       for (base::ListValue::const_iterator it = list_value->begin();
            it != list_value->end();
            ++it) {
-        DLOG_IF(ERROR, (*it)->GetType() != base::Value::Type::STRING)
-            << "Unexpected type " << (*it)->GetType();
+        DLOG_IF(ERROR, it->GetType() != base::Value::Type::STRING)
+            << "Unexpected type " << it->GetType();
         std::string str;
-        (*it)->GetAsString(&str);
+        it->GetAsString(&str);
         array_writer.AppendString(str);
       }
       variant_writer.CloseContainer(&array_writer);
diff --git a/chromeos/network/geolocation_handler.cc b/chromeos/network/geolocation_handler.cc
index a1c0b5e..7ae451c 100644
--- a/chromeos/network/geolocation_handler.cc
+++ b/chromeos/network/geolocation_handler.cc
@@ -112,7 +112,7 @@
   for (base::ListValue::const_iterator iter = technologies->begin();
        iter != technologies->end(); ++iter) {
     std::string technology;
-    (*iter)->GetAsString(&technology);
+    iter->GetAsString(&technology);
     if (technology == shill::kTypeWifi) {
       wifi_enabled_ = true;
     } else if (technology == shill::kTypeCellular) {
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 023197e7..8ada86f 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -496,8 +496,8 @@
 
   for (base::ListValue::const_iterator it = network_configs_onc.begin();
        it != network_configs_onc.end(); ++it) {
-    base::DictionaryValue* network = NULL;
-    (*it)->GetAsDictionary(&network);
+    const base::DictionaryValue* network = NULL;
+    it->GetAsDictionary(&network);
     DCHECK(network);
 
     std::string guid;
diff --git a/chromeos/network/network_sms_handler.cc b/chromeos/network/network_sms_handler.cc
index f3eb86e..42eaf41f 100644
--- a/chromeos/network/network_sms_handler.cc
+++ b/chromeos/network/network_sms_handler.cc
@@ -115,8 +115,8 @@
   delete_queue_.clear();
   for (base::ListValue::const_iterator iter = message_list.begin();
        iter != message_list.end(); ++iter) {
-    base::DictionaryValue* message = NULL;
-    if (!(*iter)->GetAsDictionary(&message))
+    const base::DictionaryValue* message = NULL;
+    if (iter->GetAsDictionary(&message))
       continue;
     MessageReceived(*message);
     double index = 0;
@@ -423,7 +423,7 @@
   for (base::ListValue::const_iterator iter = devices->begin();
        iter != devices->end(); ++iter) {
     std::string device_path;
-    (*iter)->GetAsString(&device_path);
+    iter->GetAsString(&device_path);
     if (!device_path.empty()) {
       // Request device properties.
       VLOG(1) << "GetDeviceProperties: " << device_path;
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 7581d1e1..aeba782 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -602,7 +602,7 @@
   std::set<std::string> list_entries;
   for (auto& iter : entries) {
     std::string path;
-    iter->GetAsString(&path);
+    iter.GetAsString(&path);
     if (path.empty() || path == shill::kFlimflamServicePath) {
       NET_LOG_ERROR(base::StringPrintf("Bad path in list:%d", type), path);
       continue;
diff --git a/chromeos/network/network_util.cc b/chromeos/network/network_util.cc
index 9d08c59..db4623b 100644
--- a/chromeos/network/network_util.cc
+++ b/chromeos/network/network_util.cc
@@ -137,7 +137,7 @@
   scan_results->reserve(list.GetSize());
   for (const auto& value : list) {
     const base::DictionaryValue* dict;
-    if (!value->GetAsDictionary(&dict))
+    if (!value.GetAsDictionary(&dict))
       return false;
     CellularScanResult scan_result;
     // If the network id property is not present then this network cannot be
diff --git a/chromeos/network/onc/onc_certificate_importer_impl.cc b/chromeos/network/onc/onc_certificate_importer_impl.cc
index 0542be3a..d36a4e5 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl.cc
+++ b/chromeos/network/onc/onc_certificate_importer_impl.cc
@@ -167,7 +167,7 @@
     for (base::ListValue::const_iterator it = trust_list->begin();
          it != trust_list->end(); ++it) {
       std::string trust_type;
-      if (!(*it)->GetAsString(&trust_type))
+      if (!it->GetAsString(&trust_type))
         NOTREACHED();
 
       if (trust_type == ::onc::certificate::kWeb) {
diff --git a/chromeos/network/onc/onc_mapper.cc b/chromeos/network/onc/onc_mapper.cc
index 22f5766..16c1172 100644
--- a/chromeos/network/onc/onc_mapper.cc
+++ b/chromeos/network/onc/onc_mapper.cc
@@ -120,10 +120,9 @@
   int original_index = 0;
   for (const auto& entry : onc_array) {
     std::unique_ptr<base::Value> result_entry;
-    result_entry = MapEntry(original_index,
-                            *array_signature.onc_array_entry_signature,
-                            *entry,
-                            nested_error);
+    result_entry =
+        MapEntry(original_index, *array_signature.onc_array_entry_signature,
+                 entry, nested_error);
     if (result_entry.get() != NULL)
       result_array->Append(std::move(result_entry));
     else
diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/network/onc/onc_merger.cc
index df8fd5bd..41c6b58 100644
--- a/chromeos/network/onc/onc_merger.cc
+++ b/chromeos/network/onc/onc_merger.cc
@@ -56,7 +56,7 @@
   for (base::ListValue::const_iterator it = recommended_value->begin();
        it != recommended_value->end(); ++it) {
     std::string entry;
-    if ((*it)->GetAsString(&entry))
+    if (it->GetAsString(&entry))
       result->SetBooleanWithoutPathExpansion(entry, true);
   }
 }
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc
index eacb2c3..7796c2af 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc
@@ -683,7 +683,7 @@
   for (base::ListValue::const_iterator it = list.begin(); it != list.end();
        ++it) {
     const base::DictionaryValue* shill_value = NULL;
-    if (!(*it)->GetAsDictionary(&shill_value))
+    if (!it->GetAsDictionary(&shill_value))
       continue;
     ShillToONCTranslator nested_translator(
         *shill_value, onc_source_,
diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc
index 7a797e96..4b7b7a3 100644
--- a/chromeos/network/onc/onc_utils.cc
+++ b/chromeos/network/onc/onc_utils.cc
@@ -281,9 +281,9 @@
 
 void ExpandStringsInNetworks(const StringSubstitution& substitution,
                              base::ListValue* network_configs) {
-  for (const auto& entry : *network_configs) {
+  for (auto& entry : *network_configs) {
     base::DictionaryValue* network = nullptr;
-    entry->GetAsDictionary(&network);
+    entry.GetAsDictionary(&network);
     DCHECK(network);
     ExpandStringsInOncObject(
         kNetworkConfigurationSignature, substitution, network);
@@ -407,7 +407,7 @@
   CertPEMsByGUIDMap certs_by_guid;
   for (const auto& entry : certificates) {
     const base::DictionaryValue* cert = nullptr;
-    bool entry_is_dictionary = entry->GetAsDictionary(&cert);
+    bool entry_is_dictionary = entry.GetAsDictionary(&cert);
     DCHECK(entry_is_dictionary);
 
     std::string guid;
@@ -435,9 +435,9 @@
 }
 
 void FillInHexSSIDFieldsInNetworks(base::ListValue* network_configs) {
-  for (const auto& entry : *network_configs) {
+  for (auto& entry : *network_configs) {
     base::DictionaryValue* network = nullptr;
-    entry->GetAsDictionary(&network);
+    entry.GetAsDictionary(&network);
     DCHECK(network);
     FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature, network);
   }
@@ -603,7 +603,7 @@
   std::unique_ptr<base::ListValue> pem_list(new base::ListValue);
   for (const auto& entry : *guid_ref_list) {
     std::string guid_ref;
-    bool entry_is_string = entry->GetAsString(&guid_ref);
+    bool entry_is_string = entry.GetAsString(&guid_ref);
     DCHECK(entry_is_string);
 
     std::string pem_encoded;
@@ -730,7 +730,7 @@
   for (base::ListValue::iterator it = network_configs->begin();
        it != network_configs->end(); ) {
     base::DictionaryValue* network = nullptr;
-    (*it)->GetAsDictionary(&network);
+    it->GetAsDictionary(&network);
     if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) {
       std::string guid;
       network->GetStringWithoutPathExpansion(network_config::kGUID, &guid);
@@ -857,7 +857,7 @@
   for (base::ListValue::const_iterator it = onc_exclude_domains.begin();
        it != onc_exclude_domains.end(); ++it) {
     std::string rule;
-    (*it)->GetAsString(&rule);
+    it->GetAsString(&rule);
     rules.AddRuleFromString(rule);
   }
   return rules;
@@ -1043,7 +1043,7 @@
   for (base::ListValue::const_iterator it = network_configs.begin();
        it != network_configs.end(); ++it) {
     const base::DictionaryValue* network = NULL;
-    (*it)->GetAsDictionary(&network);
+    it->GetAsDictionary(&network);
     DCHECK(network);
 
     std::string current_guid;
@@ -1061,7 +1061,7 @@
   for (base::ListValue::const_iterator it = network_configs.begin();
        it != network_configs.end(); ++it) {
     const base::DictionaryValue* network = NULL;
-    (*it)->GetAsDictionary(&network);
+    it->GetAsDictionary(&network);
     DCHECK(network);
 
     std::string type;
@@ -1187,7 +1187,7 @@
   for (base::ListValue::const_iterator it = expanded_networks->begin();
        it != expanded_networks->end(); ++it) {
     const base::DictionaryValue* network = NULL;
-    (*it)->GetAsDictionary(&network);
+    it->GetAsDictionary(&network);
     DCHECK(network);
 
     // Remove irrelevant fields.
diff --git a/chromeos/network/onc/onc_utils_unittest.cc b/chromeos/network/onc/onc_utils_unittest.cc
index 92cbceb1..8e20606 100644
--- a/chromeos/network/onc/onc_utils_unittest.cc
+++ b/chromeos/network/onc/onc_utils_unittest.cc
@@ -179,7 +179,7 @@
   test_data->GetAsList(&tests2);
   ASSERT_TRUE(tests2);
   for (auto iter1 = tests2->begin(); iter1 != tests2->end(); ++iter1)
-    list_of_tests->Append((*iter1)->CreateDeepCopy());
+    list_of_tests->Append(iter1->CreateDeepCopy());
 
   int index = 0;
   for (auto iter2 = list_of_tests->begin(); iter2 != list_of_tests->end();
@@ -187,7 +187,7 @@
     SCOPED_TRACE("Test case #" + base::IntToString(index));
 
     base::DictionaryValue* test_case = nullptr;
-    (*iter2)->GetAsDictionary(&test_case);
+    iter2->GetAsDictionary(&test_case);
     ASSERT_TRUE(test_case);
 
     base::DictionaryValue* onc_proxy_settings;
@@ -216,7 +216,7 @@
     SCOPED_TRACE("Test case #" + base::IntToString(index));
 
     base::DictionaryValue* test_case;
-    (*it)->GetAsDictionary(&test_case);
+    it->GetAsDictionary(&test_case);
 
     base::DictionaryValue* shill_proxy_config;
     test_case->GetDictionary("ProxyConfig", &shill_proxy_config);
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index d4286205ed..97150fd 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -245,7 +245,7 @@
   std::unique_ptr<base::ListValue> repaired_recommended(new base::ListValue);
   for (const auto& entry : *recommended_list) {
     std::string field_name;
-    if (!entry->GetAsString(&field_name)) {
+    if (!entry.GetAsString(&field_name)) {
       NOTREACHED();  // The types of field values are already verified.
       continue;
     }
@@ -404,7 +404,7 @@
     path_.push_back(field_name);
     for (const auto& entry : *list) {
       std::string value;
-      if (!entry->GetAsString(&value)) {
+      if (!entry.GetAsString(&value)) {
         NOTREACHED();  // The types of field values are already verified.
         continue;
       }
diff --git a/chromeos/network/policy_applicator.cc b/chromeos/network/policy_applicator.cc
index b2fec74d..66070bb 100644
--- a/chromeos/network/policy_applicator.cc
+++ b/chromeos/network/policy_applicator.cc
@@ -96,7 +96,7 @@
   for (base::ListValue::const_iterator it = entries->begin();
        it != entries->end(); ++it) {
     std::string entry;
-    (*it)->GetAsString(&entry);
+    it->GetAsString(&entry);
 
     pending_get_entry_calls_.insert(entry);
     DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
diff --git a/chromeos/network/prohibited_technologies_handler.cc b/chromeos/network/prohibited_technologies_handler.cc
index 97bdec9..69bc5039 100644
--- a/chromeos/network/prohibited_technologies_handler.cc
+++ b/chromeos/network/prohibited_technologies_handler.cc
@@ -65,7 +65,7 @@
   prohibited_technologies_.clear();
   for (const auto& item : *prohibited_list) {
     std::string prohibited_technology;
-    bool item_is_string = item->GetAsString(&prohibited_technology);
+    bool item_is_string = item.GetAsString(&prohibited_technology);
     DCHECK(item_is_string);
     std::string translated_tech =
         network_util::TranslateONCTypeToShill(prohibited_technology);
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
index 74ea70a..260c3595 100644
--- a/chromeos/network/shill_property_handler.cc
+++ b/chromeos/network/shill_property_handler.cc
@@ -365,7 +365,7 @@
   for (base::ListValue::const_iterator iter = entries.begin();
        iter != entries.end(); ++iter) {
     std::string path;
-    (*iter)->GetAsString(&path);
+    iter->GetAsString(&path);
     if (path.empty())
       continue;
 
@@ -389,7 +389,7 @@
   ShillPropertyObserverMap new_observed;
   for (const auto& entry : entries) {
     std::string path;
-    entry->GetAsString(&path);
+    entry.GetAsString(&path);
     if (path.empty())
       continue;
     auto iter = observer_map.find(path);
@@ -422,7 +422,7 @@
   for (base::ListValue::const_iterator iter = technologies.begin();
        iter != technologies.end(); ++iter) {
     std::string technology;
-    (*iter)->GetAsString(&technology);
+    iter->GetAsString(&technology);
     DCHECK(!technology.empty());
     available_technologies_.insert(technology);
   }
@@ -436,7 +436,7 @@
   for (base::ListValue::const_iterator iter = technologies.begin();
        iter != technologies.end(); ++iter) {
     std::string technology;
-    (*iter)->GetAsString(&technology);
+    iter->GetAsString(&technology);
     DCHECK(!technology.empty());
     enabled_technologies_.insert(technology);
     enabling_technologies_.erase(technology);
@@ -451,7 +451,7 @@
   for (base::ListValue::const_iterator iter = technologies.begin();
        iter != technologies.end(); ++iter) {
     std::string technology;
-    (*iter)->GetAsString(&technology);
+    iter->GetAsString(&technology);
     DCHECK(!technology.empty());
     uninitialized_technologies_.insert(technology);
   }
@@ -549,7 +549,7 @@
     return;
   for (base::ListValue::const_iterator iter = ip_configs->begin();
        iter != ip_configs->end(); ++iter) {
-    RequestIPConfig(type, path, **iter);
+    RequestIPConfig(type, path, *iter);
   }
 }
 
diff --git a/chromeos/network/shill_property_handler_unittest.cc b/chromeos/network/shill_property_handler_unittest.cc
index 0778663c..47e169e 100644
--- a/chromeos/network/shill_property_handler_unittest.cc
+++ b/chromeos/network/shill_property_handler_unittest.cc
@@ -131,7 +131,7 @@
     for (base::ListValue::const_iterator iter = entries.begin();
          iter != entries.end(); ++iter) {
       std::string path;
-      if ((*iter)->GetAsString(&path))
+      if (iter->GetAsString(&path))
         entries_[type].push_back(path);
     }
   }
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index a6a08c59..37130fc 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -431,11 +431,11 @@
     // This should just be a simple list of locale strings.
     std::vector<std::string> available_locales;
     bool found_en = false;
-    for (const std::unique_ptr<base::Value>& entry : *top_list) {
+    for (const base::Value& entry : *top_list) {
       std::string tmp;
       // Locales should have at *least* a two-character country code.  100 is an
       // arbitrary upper bound for length to protect against extreme bogosity.
-      if (!entry->GetAsString(&tmp) || tmp.size() < 2 || tmp.size() > 100) {
+      if (!entry.GetAsString(&tmp) || tmp.size() < 2 || tmp.size() > 100) {
         FailQueuedMetadataResolutions(PpdProvider::INTERNAL_ERROR);
         return;
       }
@@ -603,10 +603,10 @@
         result = PpdProvider::NOT_FOUND;
         for (const auto& entry : *top_list) {
           int device_id;
-          base::ListValue* sub_list;
+          const base::ListValue* sub_list;
 
           // Each entry should be a size-2 list with an integer and a string.
-          if (!entry->GetAsList(&sub_list) || sub_list->GetSize() != 2 ||
+          if (!entry.GetAsList(&sub_list) || sub_list->GetSize() != 2 ||
               !sub_list->GetInteger(0, &device_id) ||
               !sub_list->GetString(1, &contents) || device_id < 0 ||
               device_id > 0xffff) {
@@ -791,9 +791,9 @@
       return PpdProvider::INTERNAL_ERROR;
     }
     for (const auto& entry : *top_list) {
-      base::ListValue* sub_list;
+      const base::ListValue* sub_list;
       contents->push_back({});
-      if (!entry->GetAsList(&sub_list) || sub_list->GetSize() != 2 ||
+      if (!entry.GetAsList(&sub_list) || sub_list->GetSize() != 2 ||
           !sub_list->GetString(0, &contents->back().first) ||
           !sub_list->GetString(1, &contents->back().second)) {
         contents->clear();
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc
index 26fb751b..c7144df 100644
--- a/chromeos/system/statistics_provider.cc
+++ b/chromeos/system/statistics_provider.cc
@@ -61,8 +61,7 @@
 const char kEchoCouponEq[] = "=";
 const char kEchoCouponDelim[] = "\n";
 
-// File to get VPD info from, and key/value delimiters of the file.
-const char kVpdFile[] = "/var/log/vpd_2.0.txt";
+// Key/value delimiters for VPD file.
 const char kVpdEq[] = "=";
 const char kVpdDelim[] = "\n";
 
@@ -115,7 +114,7 @@
   bool first = true;
   for (const auto& v : *list) {
     std::string value;
-    if (!v->GetAsString(&value))
+    if (!v.GetAsString(&value))
       return false;
 
     if (first)
@@ -471,13 +470,22 @@
     std::string stub_contents =
         "\"serial_number\"=\"stub_" +
         base::Int64ToString(base::Time::Now().ToJavaTime()) + "\"\n";
-    int bytes_written = base::WriteFile(machine_info_path,
-                                        stub_contents.c_str(),
-                                        stub_contents.size());
-    // static_cast<int> is fine because stub_contents is small.
+    int bytes_written = base::WriteFile(
+        machine_info_path, stub_contents.c_str(), stub_contents.size());
     if (bytes_written < static_cast<int>(stub_contents.size())) {
-      LOG(ERROR) << "Error writing machine info stub: "
-                 << machine_info_path.value();
+      PLOG(ERROR) << "Error writing machine info stub "
+                  << machine_info_path.value();
+    }
+  }
+
+  base::FilePath vpd_path;
+  PathService::Get(chromeos::FILE_VPD, &vpd_path);
+  if (!base::SysInfo::IsRunningOnChromeOS() && !base::PathExists(vpd_path)) {
+    std::string stub_contents = "\"ActivateDate\"=\"2000-01\"\n";
+    int bytes_written =
+        base::WriteFile(vpd_path, stub_contents.c_str(), stub_contents.size());
+    if (bytes_written < static_cast<int>(stub_contents.size())) {
+      PLOG(ERROR) << "Error writing vpd stub " << vpd_path.value();
     }
   }
 
@@ -487,7 +495,7 @@
   parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile),
                                    kEchoCouponEq,
                                    kEchoCouponDelim);
-  parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile),
+  parser.GetNameValuePairsFromFile(vpd_path,
                                    kVpdEq,
                                    kVpdDelim);
 
diff --git a/components/arc/net/arc_net_host_impl.cc b/components/arc/net/arc_net_host_impl.cc
index 05cb78d1..2531cdf 100644
--- a/components/arc/net/arc_net_host_impl.cc
+++ b/components/arc/net/arc_net_host_impl.cc
@@ -374,8 +374,8 @@
   for (const auto& value : *network_properties_list) {
     mojom::WifiConfigurationPtr wc = mojom::WifiConfiguration::New();
 
-    base::DictionaryValue* network_dict = nullptr;
-    value->GetAsDictionary(&network_dict);
+    const base::DictionaryValue* network_dict = nullptr;
+    value.GetAsDictionary(&network_dict);
     DCHECK(network_dict);
 
     // kName is a post-processed version of kHexSSID.
@@ -389,7 +389,7 @@
     DCHECK(!tmp.empty());
     wc->guid = tmp;
 
-    base::DictionaryValue* wifi_dict = nullptr;
+    const base::DictionaryValue* wifi_dict = nullptr;
     network_dict->GetDictionary(onc::network_config::kWiFi, &wifi_dict);
     DCHECK(wifi_dict);
 
diff --git a/components/autofill/content/browser/risk/fingerprint.cc b/components/autofill/content/browser/risk/fingerprint.cc
index 0904140..d382553 100644
--- a/components/autofill/content/browser/risk/fingerprint.cc
+++ b/components/autofill/content/browser/risk/fingerprint.cc
@@ -86,7 +86,7 @@
     // Each item in the list is a two-element list such that the first element
     // is the font family and the second is the font name.
     const base::ListValue* font_description = NULL;
-    bool success = it->GetAsList(&font_description);
+    bool success = it.GetAsList(&font_description);
     DCHECK(success);
 
     std::string font_name;
diff --git a/components/autofill/core/browser/autofill_address_util.cc b/components/autofill/core/browser/autofill_address_util.cc
index e0e9b28..c232e002 100644
--- a/components/autofill/core/browser/autofill_address_util.cc
+++ b/components/autofill/core/browser/autofill_address_util.cc
@@ -76,6 +76,8 @@
         components[i].length_hint == AddressUiComponent::HINT_LONG) {
       line = new base::ListValue;
       address_components->Append(base::WrapUnique(line));
+      // |line| is invalidated at this point, so it needs to be reset.
+      address_components->GetList(address_components->GetSize() - 1, &line);
     }
 
     std::unique_ptr<base::DictionaryValue> component(new base::DictionaryValue);
diff --git a/components/bookmarks/browser/bookmark_expanded_state_tracker.cc b/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
index c08d8335..dd3070b 100644
--- a/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
+++ b/components/bookmarks/browser/bookmark_expanded_state_tracker.cc
@@ -50,7 +50,7 @@
     std::string value;
     int64_t node_id;
     const BookmarkNode* node;
-    if ((*i)->GetAsString(&value) && base::StringToInt64(value, &node_id) &&
+    if (i->GetAsString(&value) && base::StringToInt64(value, &node_id) &&
         (node = GetBookmarkNodeByID(bookmark_model_, node_id)) != NULL &&
         node->is_folder()) {
       nodes.insert(node);
diff --git a/components/browser_watcher/postmortem_report_collector.h b/components/browser_watcher/postmortem_report_collector.h
index aab3ab8..000b14e 100644
--- a/components/browser_watcher/postmortem_report_collector.h
+++ b/components/browser_watcher/postmortem_report_collector.h
@@ -70,7 +70,7 @@
       LogCollection);
   FRIEND_TEST_ALL_PREFIXES(
       PostmortemReportCollectorCollectionFromGlobalTrackerTest,
-      GlobalUserDataCollection);
+      ProcessUserDataCollection);
   FRIEND_TEST_ALL_PREFIXES(
       PostmortemReportCollectorCollectionFromGlobalTrackerTest,
       FieldTrialCollection);
diff --git a/components/browser_watcher/postmortem_report_collector_unittest.cc b/components/browser_watcher/postmortem_report_collector_unittest.cc
index 12dd88d..a022c82 100644
--- a/components/browser_watcher/postmortem_report_collector_unittest.cc
+++ b/components/browser_watcher/postmortem_report_collector_unittest.cc
@@ -56,6 +56,9 @@
 const char kVersionNumber[] = "TestVersionNumber";
 const char kChannelName[] = "TestChannel";
 
+// The tracker creates some data entries internally.
+const size_t kInternalProcessDatums = 1;
+
 void ContainsKeyValue(
     const google::protobuf::Map<std::string, TypedValue>& data,
     const std::string& key,
@@ -578,22 +581,25 @@
 }
 
 TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
-       GlobalUserDataCollection) {
+       ProcessUserDataCollection) {
   const char string1[] = "foo";
   const char string2[] = "bar";
 
-  // Record some global user data.
+  // Record some process user data.
   GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
                                         "", 3);
-  ActivityUserData& global_data = GlobalActivityTracker::Get()->global_data();
-  global_data.Set("raw", "foo", 3);
-  global_data.SetString("string", "bar");
-  global_data.SetChar("char", '9');
-  global_data.SetInt("int", -9999);
-  global_data.SetUint("uint", 9999);
-  global_data.SetBool("bool", true);
-  global_data.SetReference("ref", string1, strlen(string1));
-  global_data.SetStringReference("sref", string2);
+  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+  ActivityUserData::Snapshot snapshot;
+  ASSERT_TRUE(process_data.CreateSnapshot(&snapshot));
+  ASSERT_EQ(kInternalProcessDatums, snapshot.size());
+  process_data.Set("raw", "foo", 3);
+  process_data.SetString("string", "bar");
+  process_data.SetChar("char", '9');
+  process_data.SetInt("int", -9999);
+  process_data.SetUint("uint", 9999);
+  process_data.SetBool("bool", true);
+  process_data.SetReference("ref", string1, strlen(string1));
+  process_data.SetStringReference("sref", string2);
 
   // Collect the stability report.
   PostmortemReportCollector collector(kProductName, kVersionNumber,
@@ -603,7 +609,7 @@
 
   // Validate the report's user data.
   const auto& collected_data = report.global_data();
-  ASSERT_EQ(12U, collected_data.size());
+  ASSERT_EQ(kInternalProcessDatums + 12U, collected_data.size());
 
   ASSERT_TRUE(base::ContainsKey(collected_data, "raw"));
   EXPECT_EQ(TypedValue::kBytesValue, collected_data.at("raw").value_case());
@@ -659,10 +665,10 @@
   // Record some data.
   GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
                                         "", 3);
-  ActivityUserData& global_data = GlobalActivityTracker::Get()->global_data();
-  global_data.SetString("string", "bar");
-  global_data.SetString("FieldTrial.string", "bar");
-  global_data.SetString("FieldTrial.foo", "bar");
+  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+  process_data.SetString("string", "bar");
+  process_data.SetString("FieldTrial.string", "bar");
+  process_data.SetString("FieldTrial.foo", "bar");
 
   // Collect the stability report.
   PostmortemReportCollector collector(kProductName, kVersionNumber,
@@ -680,7 +686,7 @@
 
   // Expect 5 key/value pairs (including product details).
   const auto& collected_data = report.global_data();
-  EXPECT_EQ(5U, collected_data.size());
+  EXPECT_EQ(kInternalProcessDatums + 5U, collected_data.size());
   EXPECT_TRUE(base::ContainsKey(collected_data, "string"));
 }
 
@@ -735,8 +741,8 @@
   // Setup.
   GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
                                         "", 3);
-  ActivityUserData& global_data = GlobalActivityTracker::Get()->global_data();
-  global_data.SetInt(kStabilityStartTimestamp, 12345LL);
+  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+  process_data.SetInt(kStabilityStartTimestamp, 12345LL);
 
   // Collect.
   MockSystemSessionAnalyzer analyzer;
diff --git a/components/browser_watcher/postmortem_report_extractor.cc b/components/browser_watcher/postmortem_report_extractor.cc
index c5f2b3c..d21abae3 100644
--- a/components/browser_watcher/postmortem_report_extractor.cc
+++ b/components/browser_watcher/postmortem_report_extractor.cc
@@ -213,17 +213,18 @@
   if (!global_analyzer)
     return ANALYZER_CREATION_FAILED;
 
-  // Early exit if there is no data.
-  std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
-  ActivityUserData::Snapshot global_data_snapshot =
-      global_analyzer->GetGlobalDataSnapshot();
-
   // Extract data for only the first process.
   // TODO(manzagop): Extend this to all processes.
   int64_t pid = global_analyzer->GetFirstProcess();
+
+  // Early exit if there is no data.
+  std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
+  ActivityUserData::Snapshot process_data_snapshot =
+      global_analyzer->GetProcessDataSnapshot(pid);
+
   ThreadActivityAnalyzer* thread_analyzer =
       global_analyzer->GetFirstAnalyzer(pid);
-  if (log_messages.empty() && global_data_snapshot.empty() &&
+  if (log_messages.empty() && process_data_snapshot.empty() &&
       !thread_analyzer) {
     return DEBUG_FILE_NO_DATA;
   }
@@ -236,7 +237,7 @@
   // Collect global user data.
   google::protobuf::Map<std::string, TypedValue>& global_data =
       *(report->mutable_global_data());
-  CollectUserData(global_data_snapshot, &global_data, report);
+  CollectUserData(process_data_snapshot, &global_data, report);
 
   // Collect thread activity data.
   // Note: a single process is instrumented.
diff --git a/components/browser_watcher/stability_debugging.cc b/components/browser_watcher/stability_debugging.cc
index 59dc4fb..c7919d2b 100644
--- a/components/browser_watcher/stability_debugging.cc
+++ b/components/browser_watcher/stability_debugging.cc
@@ -97,7 +97,7 @@
   if (!global_tracker)
     return;  // Activity tracking isn't enabled.
 
-  global_tracker->global_data().SetInt(name, value);
+  global_tracker->process_data().SetInt(name, value);
 }
 
 }  // namespace browser_watcher
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc
index ccddd37..18609af 100644
--- a/components/cast_certificate/cast_cert_validator.cc
+++ b/components/cast_certificate/cast_cert_validator.cc
@@ -160,19 +160,9 @@
   return false;
 }
 
-// Returns true if the extended key usage list |ekus| contains client auth.
-bool HasClientAuth(const std::vector<net::der::Input>& ekus) {
-  for (const auto& oid : ekus) {
-    if (oid == net::ClientAuth())
-      return true;
-  }
-  return false;
-}
-
 // Checks properties on the target certificate.
 //
 //   * The Key Usage must include Digital Signature
-//   * The Extended Key Usage must include TLS Client Auth
 //   * May have the policy 1.3.6.1.4.1.11129.2.5.2 to indicate it
 //     is an audio-only device.
 WARN_UNUSED_RESULT bool CheckTargetCertificate(
@@ -187,11 +177,6 @@
   if (!cert->key_usage().AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
     return false;
 
-  // Ensure Extended Key Usage contains client auth.
-  if (!cert->has_extended_key_usage() ||
-      !HasClientAuth(cert->extended_key_usage()))
-    return false;
-
   // Check for an optional audio-only policy extension.
   *policy = CastDeviceCertPolicy::NONE;
   if (cert->has_policy_oids()) {
@@ -283,7 +268,7 @@
   net::CertPathBuilder::Result result;
   net::CertPathBuilder path_builder(target_cert.get(), trust_store,
                                     signature_policy.get(), verification_time,
-                                    &result);
+                                    net::KeyPurpose::CLIENT_AUTH, &result);
   path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
   path_builder.Run();
   if (!result.HasValidPath()) {
diff --git a/components/cast_certificate/cast_crl.cc b/components/cast_certificate/cast_crl.cc
index fa95ae3..7d866e37 100644
--- a/components/cast_certificate/cast_crl.cc
+++ b/components/cast_certificate/cast_crl.cc
@@ -147,7 +147,7 @@
   net::CertPathBuilder::Result result;
   net::CertPathBuilder path_builder(parsed_cert.get(), trust_store,
                                     signature_policy.get(), verification_time,
-                                    &result);
+                                    net::KeyPurpose::ANY_EKU, &result);
   path_builder.Run();
   if (!result.HasValidPath()) {
     VLOG(2) << "CRL - Issuer certificate verification failed.";
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index 651d177..bbfef49 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -370,12 +370,14 @@
           &DataReductionProxyCompressionStats::OnDataUsageReportingPrefChanged,
           weak_factory_.GetWeakPtr()));
 
+#if defined(OS_ANDROID)
   if (!base::FeatureList::IsEnabled(features::kDataReductionSiteBreakdown)) {
     // If the user is moved out of the experiment make sure that data usage
     // reporting is not enabled and the map is cleared.
     SetDataUsageReportingEnabled(false);
     DeleteHistoricalDataUsage();
   }
+#endif
 
   if (data_usage_reporting_enabled_.GetValue()) {
     current_data_usage_load_status_ = LOADING;
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
index 5283b45..fd6ddc7 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
@@ -55,7 +55,7 @@
   std::vector<base::StringPiece> values;
   for (const auto& value : *list_value) {
     base::StringPiece value_string;
-    if (!value->GetAsString(&value_string))
+    if (!value.GetAsString(&value_string))
       return std::string();
 
     values.push_back(value_string);
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index 5192a11..9047cdc 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -757,6 +757,9 @@
         l10n_util::GetStringUTF16(body_message_id));
   }
   list->Append(base::WrapUnique(suggestion_list_item));
+  // |suggestion_list_item| is invalidated at this point, so it needs to be
+  // reset.
+  list->GetDictionary(list->GetSize() - 1, &suggestion_list_item);
   return suggestion_list_item;
 }
 
diff --git a/components/flags_ui/pref_service_flags_storage.cc b/components/flags_ui/pref_service_flags_storage.cc
index 3c6ddc4d..2153c1bf6 100644
--- a/components/flags_ui/pref_service_flags_storage.cc
+++ b/components/flags_ui/pref_service_flags_storage.cc
@@ -26,7 +26,7 @@
   for (base::ListValue::const_iterator it = enabled_experiments->begin();
        it != enabled_experiments->end(); ++it) {
     std::string experiment_name;
-    if (!(*it)->GetAsString(&experiment_name)) {
+    if (!it->GetAsString(&experiment_name)) {
       LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments;
       continue;
     }
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
index 4b4cfa9..31bf8ae6 100644
--- a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -413,9 +413,9 @@
     return false;
   }
 
-  for (const std::unique_ptr<base::Value>& value : *list) {
-    base::DictionaryValue* dictionary;
-    if (!value->GetAsDictionary(&dictionary)) {
+  for (const base::Value& value : *list) {
+    const base::DictionaryValue* dictionary;
+    if (!value.GetAsDictionary(&dictionary)) {
       LOG(DFATAL) << "Failed to parse category data from prefs param "
                   << prefs::kClickBasedCategoryRankerOrderWithClicks
                   << " into dictionary.";
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index cb54bbb..9d4955e 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -606,10 +606,10 @@
 
   const base::ListValue* list =
       pref_service_->GetList(prefs::kDismissedCategories);
-  for (const std::unique_ptr<base::Value>& entry : *list) {
+  for (const base::Value& entry : *list) {
     int id = 0;
-    if (!entry->GetAsInteger(&id)) {
-      DLOG(WARNING) << "Invalid category pref value: " << *entry;
+    if (!entry.GetAsInteger(&id)) {
+      DLOG(WARNING) << "Invalid category pref value: " << entry;
       continue;
     }
 
diff --git a/components/ntp_snippets/pref_util.cc b/components/ntp_snippets/pref_util.cc
index a6288f9..3973cda 100644
--- a/components/ntp_snippets/pref_util.cc
+++ b/components/ntp_snippets/pref_util.cc
@@ -16,9 +16,9 @@
                                                 const std::string& pref_name) {
   std::set<std::string> dismissed_ids;
   const base::ListValue* list = pref_service.GetList(pref_name);
-  for (const std::unique_ptr<base::Value>& value : *list) {
+  for (const base::Value& value : *list) {
     std::string dismissed_id;
-    bool success = value->GetAsString(&dismissed_id);
+    bool success = value.GetAsString(&dismissed_id);
     DCHECK(success) << "Failed to parse dismissed id from prefs param "
                     << pref_name << " into string.";
     dismissed_ids.insert(dismissed_id);
diff --git a/components/ntp_snippets/remote/remote_suggestion.cc b/components/ntp_snippets/remote/remote_suggestion.cc
index a60c072..988d61a9 100644
--- a/components/ntp_snippets/remote/remote_suggestion.cc
+++ b/components/ntp_snippets/remote/remote_suggestion.cc
@@ -119,7 +119,7 @@
   std::vector<SnippetSource> sources;
   for (const auto& value : *corpus_infos_list) {
     const base::DictionaryValue* dict_value = nullptr;
-    if (!value->GetAsDictionary(&dict_value)) {
+    if (!value.GetAsDictionary(&dict_value)) {
       DLOG(WARNING) << "Invalid source info for article " << primary_id;
       continue;
     }
@@ -228,7 +228,7 @@
   std::vector<std::string> parsed_ids;
   for (const auto& value : *ids) {
     std::string id;
-    if (!value->GetAsString(&id)) {
+    if (!value.GetAsString(&id)) {
       return nullptr;
     }
     parsed_ids.push_back(id);
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
index 8bd852c..b07a116 100644
--- a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -112,7 +112,7 @@
                                  const base::Time& fetch_time) {
   for (const auto& value : list) {
     const base::DictionaryValue* dict = nullptr;
-    if (!value->GetAsDictionary(&dict)) {
+    if (!value.GetAsDictionary(&dict)) {
       return false;
     }
 
@@ -458,7 +458,7 @@
     std::string utf8_title;
     int remote_category_id = -1;
     const base::DictionaryValue* category_value = nullptr;
-    if (!(v->GetAsDictionary(&category_value) &&
+    if (!(v.GetAsDictionary(&category_value) &&
           category_value->GetString("localizedTitle", &utf8_title) &&
           category_value->GetInteger("id", &remote_category_id) &&
           (remote_category_id > 0))) {
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index af67c654..2763aa4 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -1191,29 +1191,29 @@
 
   const base::ListValue* list =
       pref_service_->GetList(prefs::kRemoteSuggestionCategories);
-  for (const std::unique_ptr<base::Value>& entry : *list) {
+  for (const base::Value& entry : *list) {
     const base::DictionaryValue* dict = nullptr;
-    if (!entry->GetAsDictionary(&dict)) {
-      DLOG(WARNING) << "Invalid category pref value: " << *entry;
+    if (!entry.GetAsDictionary(&dict)) {
+      DLOG(WARNING) << "Invalid category pref value: " << entry;
       continue;
     }
     int id = 0;
     if (!dict->GetInteger(kCategoryContentId, &id)) {
       DLOG(WARNING) << "Invalid category pref value, missing '"
-                    << kCategoryContentId << "': " << *entry;
+                    << kCategoryContentId << "': " << entry;
       continue;
     }
     base::string16 title;
     if (!dict->GetString(kCategoryContentTitle, &title)) {
       DLOG(WARNING) << "Invalid category pref value, missing '"
-                    << kCategoryContentTitle << "': " << *entry;
+                    << kCategoryContentTitle << "': " << entry;
       continue;
     }
     bool included_in_last_server_response = false;
     if (!dict->GetBoolean(kCategoryContentProvidedByServer,
                           &included_in_last_server_response)) {
       DLOG(WARNING) << "Invalid category pref value, missing '"
-                    << kCategoryContentProvidedByServer << "': " << *entry;
+                    << kCategoryContentProvidedByServer << "': " << entry;
       continue;
     }
     bool allow_fetching_more_results = false;
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 6942da88..c586afe 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -97,6 +97,7 @@
     "payment_manifest_downloader_unittest.cc",
     "payment_request_spec_unittest.cc",
     "payment_request_state_unittest.cc",
+    "payment_response_helper_unittest.cc",
     "payments_validators_unittest.cc",
   ]
 
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
index e33ade4..adcb3d29 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
@@ -79,5 +79,5 @@
     private static native void nativeDownloadPaymentMethodManifest(
             WebContents webContents, URI methodName, ManifestDownloadCallback callback);
     private static native void nativeDownloadWebAppManifest(
-            WebContents webContents, URI methodName, ManifestDownloadCallback callback);
+            WebContents webContents, URI webAppManifestUri, ManifestDownloadCallback callback);
 }
diff --git a/components/payments/content/android/payment_manifest_parser_android.cc b/components/payments/content/android/payment_manifest_parser_android.cc
index f32abb6..65a3e26 100644
--- a/components/payments/content/android/payment_manifest_parser_android.cc
+++ b/components/payments/content/android/payment_manifest_parser_android.cc
@@ -41,10 +41,11 @@
             env, web_app_manifest_urls.size());
 
     for (size_t i = 0; i < web_app_manifest_urls.size(); ++i) {
-      DCHECK(Java_PaymentManifestParser_addUri(
+      bool is_valid_uri = Java_PaymentManifestParser_addUri(
           env, juris.obj(), base::checked_cast<int>(i),
           base::android::ConvertUTF8ToJavaString(
-              env, web_app_manifest_urls[i].spec())));
+              env, web_app_manifest_urls[i].spec()));
+      DCHECK(is_valid_uri);
     }
 
     // Can trigger synchronous deletion of PaymentManifestParserAndroid.
diff --git a/components/payments/content/payment_manifest_parser_host.cc b/components/payments/content/payment_manifest_parser_host.cc
index 97cead1..0af9a38 100644
--- a/components/payments/content/payment_manifest_parser_host.cc
+++ b/components/payments/content/payment_manifest_parser_host.cc
@@ -82,9 +82,6 @@
     return;
   }
 
-  PaymentMethodCallback callback = std::move(pending_callback_it->second);
-  pending_payment_method_callbacks_.erase(pending_callback_it);
-
   const size_t kMaximumNumberOfWebAppUrls = 100U;
   if (web_app_manifest_urls.size() > kMaximumNumberOfWebAppUrls) {
     // If more than 100 items, then something went wrong in the utility
@@ -102,6 +99,9 @@
     }
   }
 
+  PaymentMethodCallback callback = std::move(pending_callback_it->second);
+  pending_payment_method_callbacks_.erase(pending_callback_it);
+
   // Can trigger synchronous deletion of this object, so can't access any of
   // the member variables after this block.
   std::move(callback).Run(web_app_manifest_urls);
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 81d2545..4471b604 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -14,6 +14,7 @@
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/payment_response_helper.h"
 #include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_request_delegate.h"
 #include "components/payments/core/profile_util.h"
 
@@ -39,6 +40,11 @@
 }
 PaymentRequestState::~PaymentRequestState() {}
 
+void PaymentRequestState::OnPaymentResponseReady(
+    mojom::PaymentResponsePtr payment_response) {
+  delegate_->OnPaymentResponseAvailable(std::move(payment_response));
+}
+
 bool PaymentRequestState::CanMakePayment() const {
   for (const std::unique_ptr<PaymentInstrument>& instrument :
        available_instruments_) {
@@ -60,60 +66,13 @@
   observers_.RemoveObserver(observer);
 }
 
-// TODO(sebsg): Move this to the PaymentResponseHelper.
-void PaymentRequestState::OnInstrumentDetailsReady(
-    const std::string& method_name,
-    const std::string& stringified_details) {
-  mojom::PaymentResponsePtr payment_response = mojom::PaymentResponse::New();
-
-  // Make sure that we return the method name that the merchant specified for
-  // this instrument: cards can be either specified through their name (e.g.,
-  // "visa") or through basic-card's supportedNetworks.
-  payment_response->method_name =
-      spec_->IsMethodSupportedThroughBasicCard(method_name)
-          ? kBasicCardMethodName
-          : method_name;
-  payment_response->stringified_details = stringified_details;
-
-  // Shipping Address section
-  if (spec_->request_shipping()) {
-    DCHECK(selected_shipping_profile_);
-    payment_response->shipping_address =
-        PaymentResponseHelper::GetMojomPaymentAddressFromAutofillProfile(
-            selected_shipping_profile_, app_locale_);
-
-    DCHECK(spec_->selected_shipping_option());
-    payment_response->shipping_option = spec_->selected_shipping_option()->id;
-  }
-
-  // Contact Details section.
-  if (spec_->request_payer_name()) {
-    DCHECK(selected_contact_profile_);
-    payment_response->payer_name =
-        base::UTF16ToUTF8(selected_contact_profile_->GetInfo(
-            autofill::AutofillType(autofill::NAME_FULL), app_locale_));
-  }
-  if (spec_->request_payer_email()) {
-    DCHECK(selected_contact_profile_);
-    payment_response->payer_email = base::UTF16ToUTF8(
-        selected_contact_profile_->GetRawInfo(autofill::EMAIL_ADDRESS));
-  }
-  if (spec_->request_payer_phone()) {
-    DCHECK(selected_contact_profile_);
-    // TODO(crbug.com/705945): Format phone number according to spec.
-    payment_response->payer_phone =
-        base::UTF16ToUTF8(selected_contact_profile_->GetRawInfo(
-            autofill::PHONE_HOME_WHOLE_NUMBER));
-  }
-
-  delegate_->OnPaymentResponseAvailable(std::move(payment_response));
-}
-
 void PaymentRequestState::GeneratePaymentResponse() {
   DCHECK(is_ready_to_pay());
-  // Fetch the instrument details, will call back into
-  // PaymentRequest::OnInstrumentDetailsReady.
-  selected_instrument_->InvokePaymentApp(this);
+
+  // Once the response is ready, will call back into OnPaymentResponseReady.
+  response_helper_ = base::MakeUnique<PaymentResponseHelper>(
+      app_locale_, spec_, selected_instrument_, selected_shipping_profile_,
+      selected_contact_profile_, this);
 }
 
 void PaymentRequestState::SetSelectedShippingOption(
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index f1d067bb..4f5f96a 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -11,8 +11,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "components/payments/content/payment_request.mojom.h"
-#include "components/payments/core/autofill_payment_instrument.h"
-#include "components/payments/core/payment_instrument.h"
+#include "components/payments/content/payment_response_helper.h"
 
 namespace autofill {
 class AutofillProfile;
@@ -21,6 +20,7 @@
 
 namespace payments {
 
+class PaymentInstrument;
 class PaymentRequestDelegate;
 class PaymentRequestSpec;
 
@@ -28,7 +28,7 @@
 // user is ready to pay. Uses information from the PaymentRequestSpec, which is
 // what the merchant has specified, as input into the "is ready to pay"
 // computation.
-class PaymentRequestState : public PaymentInstrument::Delegate {
+class PaymentRequestState : public PaymentResponseHelper::Delegate {
  public:
   // Any class call add itself as Observer via AddObserver() and receive
   // notification about the state changing.
@@ -66,18 +66,16 @@
                       PaymentRequestDelegate* payment_request_delegate);
   ~PaymentRequestState() override;
 
+  // PaymentResponseHelper::Delegate
+  void OnPaymentResponseReady(
+      mojom::PaymentResponsePtr payment_response) override;
+
   // Returns whether the user has at least one instrument that satisfies the
   // specified supported payment methods.
   bool CanMakePayment() const;
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // PaymentInstrument::Delegate:
-  void OnInstrumentDetailsReady(
-      const std::string& method_name,
-      const std::string& stringified_details) override;
-  void OnInstrumentDetailsError() override {}
-
   // Initiates the generation of the PaymentResponse. Callers should check
   // |is_ready_to_pay|, which is inexpensive.
   void GeneratePaymentResponse();
@@ -174,6 +172,8 @@
 
   PaymentRequestDelegate* payment_request_delegate_;
 
+  std::unique_ptr<PaymentResponseHelper> response_helper_;
+
   base::ObserverList<Observer> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestState);
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 6497a6c0..8509777 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -14,68 +14,22 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/content/payment_request.mojom.h"
 #include "components/payments/content/payment_request_spec.h"
-#include "components/payments/core/payment_request_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace payments {
 
-class FakePaymentRequestDelegate : public PaymentRequestDelegate {
- public:
-  FakePaymentRequestDelegate(
-      autofill::PersonalDataManager* personal_data_manager)
-      : personal_data_manager_(personal_data_manager), locale_("en-US") {}
-  void ShowDialog(PaymentRequest* request) override {}
-
-  void CloseDialog() override {}
-
-  void ShowErrorMessage() override {}
-
-  autofill::PersonalDataManager* GetPersonalDataManager() override {
-    return personal_data_manager_;
-  }
-
-  const std::string& GetApplicationLocale() const override { return locale_; }
-
-  bool IsIncognito() const override { return false; }
-
-  void DoFullCardRequest(
-      const autofill::CreditCard& credit_card,
-      base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
-          result_delegate) override {
-    result_delegate->OnFullCardRequestSucceeded(credit_card,
-                                                base::ASCIIToUTF16("123"));
-  }
-
- private:
-  autofill::PersonalDataManager* personal_data_manager_;
-  std::string locale_;
-  DISALLOW_COPY_AND_ASSIGN(FakePaymentRequestDelegate);
-};
-
 class PaymentRequestStateTest : public testing::Test,
                                 public PaymentRequestState::Observer,
                                 public PaymentRequestState::Delegate {
  protected:
   PaymentRequestStateTest()
       : num_on_selected_information_changed_called_(0),
-        payment_request_delegate_(
-            new FakePaymentRequestDelegate(&test_personal_data_manager_)),
         address_(autofill::test::GetFullProfile()),
-        credit_card_visa_(autofill::test::GetCreditCard()),
-        credit_card_amex_(autofill::test::GetCreditCard2()) {
+        credit_card_visa_(autofill::test::GetCreditCard()) {
     test_personal_data_manager_.AddTestingProfile(&address_);
     credit_card_visa_.set_billing_address_id(address_.guid());
     credit_card_visa_.set_use_count(5u);
     test_personal_data_manager_.AddTestingCreditCard(&credit_card_visa_);
-    credit_card_amex_.set_billing_address_id(address_.guid());
-    credit_card_amex_.set_use_count(1u);
-    test_personal_data_manager_.AddTestingCreditCard(&credit_card_amex_);
-    // Add an expired JCB card here.
-    credit_card_jcb_ = autofill::test::GetCreditCard();
-    credit_card_jcb_.SetNumber(base::ASCIIToUTF16("3530111333300000"));
-    credit_card_jcb_.set_billing_address_id(address_.guid());
-    credit_card_jcb_.set_use_count(1u);
-    credit_card_jcb_.SetExpirationDateFromString(base::ASCIIToUTF16("01/17"));
   }
   ~PaymentRequestStateTest() override {}
 
@@ -100,8 +54,7 @@
         std::move(options), std::move(details), std::move(method_data), nullptr,
         "en-US");
     state_ = base::MakeUnique<PaymentRequestState>(
-        spec_.get(), this, "en-US", &test_personal_data_manager_,
-        payment_request_delegate_.get());
+        spec_.get(), this, "en-US", &test_personal_data_manager_, nullptr);
     state_->AddObserver(this);
   }
 
@@ -144,13 +97,10 @@
   int num_on_selected_information_changed_called_;
   mojom::PaymentResponsePtr payment_response_;
   autofill::TestPersonalDataManager test_personal_data_manager_;
-  std::unique_ptr<FakePaymentRequestDelegate> payment_request_delegate_;
 
   // Test data.
   autofill::AutofillProfile address_;
   autofill::CreditCard credit_card_visa_;
-  autofill::CreditCard credit_card_amex_;
-  autofill::CreditCard credit_card_jcb_;
 };
 
 TEST_F(PaymentRequestStateTest, CanMakePayment) {
@@ -297,142 +247,4 @@
   EXPECT_TRUE(state()->is_ready_to_pay());
 }
 
-// Test generating a PaymentResponse.
-TEST_F(PaymentRequestStateTest, GeneratePaymentResponse_SupportedMethod) {
-  // Default options (no shipping, no contact info).
-  RecreateStateWithOptions(mojom::PaymentOptions::New());
-  state()->SetSelectedInstrument(state()->available_instruments()[0].get());
-  EXPECT_EQ(1, num_on_selected_information_changed_called());
-  EXPECT_TRUE(state()->is_ready_to_pay());
-
-  // TODO(mathp): Currently synchronous, when async will need a RunLoop.
-  // "visa" is specified directly in the supportedMethods so it is returned
-  // as the method name.
-  state()->GeneratePaymentResponse();
-  EXPECT_EQ("visa", response()->method_name);
-  EXPECT_EQ(
-      "{\"billingAddress\":"
-      "{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
-      "\"city\":\"Elysium\","
-      "\"country\":\"US\","
-      "\"organization\":\"Underworld\","
-      "\"phone\":\"16502111111\","
-      "\"postalCode\":\"91111\","
-      "\"recipient\":\"John H. Doe\","
-      "\"region\":\"CA\"},"
-      "\"cardNumber\":\"4111111111111111\","
-      "\"cardSecurityCode\":\"123\","
-      "\"cardholderName\":\"Test User\","
-      "\"expiryMonth\":\"11\","
-      "\"expiryYear\":\"2022\"}",
-      response()->stringified_details);
-}
-
-// Test generating a PaymentResponse when the method is specified through
-// "basic-card".
-TEST_F(PaymentRequestStateTest, GeneratePaymentResponse_BasicCard) {
-  // The method data supports visa through basic-card.
-  mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
-  entry->supported_methods.push_back("basic-card");
-  entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
-  std::vector<mojom::PaymentMethodDataPtr> method_data;
-  method_data.push_back(std::move(entry));
-  RecreateStateWithOptionsAndDetails(mojom::PaymentOptions::New(),
-                                     mojom::PaymentDetails::New(),
-                                     std::move(method_data));
-
-  EXPECT_TRUE(state()->is_ready_to_pay());
-
-  // TODO(mathp): Currently synchronous, when async will need a RunLoop.
-  // "basic-card" is specified so it is returned as the method name.
-  state()->GeneratePaymentResponse();
-  EXPECT_EQ("basic-card", response()->method_name);
-  EXPECT_EQ(
-      "{\"billingAddress\":"
-      "{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
-      "\"city\":\"Elysium\","
-      "\"country\":\"US\","
-      "\"organization\":\"Underworld\","
-      "\"phone\":\"16502111111\","
-      "\"postalCode\":\"91111\","
-      "\"recipient\":\"John H. Doe\","
-      "\"region\":\"CA\"},"
-      "\"cardNumber\":\"4111111111111111\","
-      "\"cardSecurityCode\":\"123\","
-      "\"cardholderName\":\"Test User\","
-      "\"expiryMonth\":\"11\","
-      "\"expiryYear\":\"2022\"}",
-      response()->stringified_details);
-}
-
-// Tests the the generated PaymentResponse has the correct values for the
-// shipping address.
-TEST_F(PaymentRequestStateTest, GeneratePaymentResponse_ShippingAddress) {
-  // Setup so that a shipping address is requested.
-  std::vector<mojom::PaymentShippingOptionPtr> shipping_options;
-  mojom::PaymentShippingOptionPtr option = mojom::PaymentShippingOption::New();
-  option->id = "option:1";
-  option->selected = true;
-  shipping_options.push_back(std::move(option));
-  mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
-  details->shipping_options = std::move(shipping_options);
-  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
-  options->request_shipping = true;
-  RecreateStateWithOptionsAndDetails(std::move(options), std::move(details),
-                                     GetMethodDataForVisa());
-
-  EXPECT_TRUE(state()->is_ready_to_pay());
-  state()->GeneratePaymentResponse();
-
-  // Check that all the expected values were set.
-  EXPECT_EQ("US", response()->shipping_address->country);
-  EXPECT_EQ("666 Erebus St.", response()->shipping_address->address_line[0]);
-  EXPECT_EQ("Apt 8", response()->shipping_address->address_line[1]);
-  EXPECT_EQ("CA", response()->shipping_address->region);
-  EXPECT_EQ("Elysium", response()->shipping_address->city);
-  EXPECT_EQ("", response()->shipping_address->dependent_locality);
-  EXPECT_EQ("91111", response()->shipping_address->postal_code);
-  EXPECT_EQ("", response()->shipping_address->sorting_code);
-  EXPECT_EQ("", response()->shipping_address->language_code);
-  EXPECT_EQ("Underworld", response()->shipping_address->organization);
-  EXPECT_EQ("John H. Doe", response()->shipping_address->recipient);
-  EXPECT_EQ("16502111111", response()->shipping_address->phone);
-}
-
-// Tests the the generated PaymentResponse has the correct values for the
-// contact details when all values are requested.
-TEST_F(PaymentRequestStateTest, GeneratePaymentResponse_ContactDetails_All) {
-  // Request all contact detail values.
-  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
-  options->request_payer_name = true;
-  options->request_payer_phone = true;
-  options->request_payer_email = true;
-  RecreateStateWithOptions(std::move(options));
-
-  EXPECT_TRUE(state()->is_ready_to_pay());
-  state()->GeneratePaymentResponse();
-
-  // Check that all the expected values were set.
-  EXPECT_EQ("John H. Doe", response()->payer_name.value());
-  EXPECT_EQ("16502111111", response()->payer_phone.value());
-  EXPECT_EQ("johndoe@hades.com", response()->payer_email.value());
-}
-
-// Tests the the generated PaymentResponse has the correct values for the
-// contact details when all values are requested.
-TEST_F(PaymentRequestStateTest, GeneratePaymentResponse_ContactDetails_Some) {
-  // Request one contact detail value.
-  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
-  options->request_payer_name = true;
-  RecreateStateWithOptions(std::move(options));
-
-  EXPECT_TRUE(state()->is_ready_to_pay());
-  state()->GeneratePaymentResponse();
-
-  // Check that the name was set, but not the other values.
-  EXPECT_EQ("John H. Doe", response()->payer_name.value());
-  EXPECT_FALSE(response()->payer_phone.has_value());
-  EXPECT_FALSE(response()->payer_email.has_value());
-}
-
 }  // namespace payments
diff --git a/components/payments/content/payment_response_helper.cc b/components/payments/content/payment_response_helper.cc
index bbec2f3..997731f6 100644
--- a/components/payments/content/payment_response_helper.cc
+++ b/components/payments/content/payment_response_helper.cc
@@ -8,10 +8,32 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_type.h"
+#include "components/payments/content/payment_request_spec.h"
 
 namespace payments {
 
-PaymentResponseHelper::PaymentResponseHelper(){};
+PaymentResponseHelper::PaymentResponseHelper(
+    const std::string& app_locale,
+    PaymentRequestSpec* spec,
+    PaymentInstrument* selected_instrument,
+    autofill::AutofillProfile* selected_shipping_profile,
+    autofill::AutofillProfile* selected_contact_profile,
+    Delegate* delegate)
+    : app_locale_(app_locale),
+      spec_(spec),
+      delegate_(delegate),
+      selected_instrument_(selected_instrument),
+      selected_shipping_profile_(selected_shipping_profile),
+      selected_contact_profile_(selected_contact_profile) {
+  DCHECK(spec_);
+  DCHECK(selected_instrument_);
+  DCHECK(delegate_);
+
+  // Start to get the instrument details. Will call back into
+  // OnInstrumentDetailsReady.
+  selected_instrument_->InvokePaymentApp(this);
+};
+
 PaymentResponseHelper::~PaymentResponseHelper(){};
 
 // static
@@ -51,4 +73,52 @@
   return payment_address;
 }
 
+void PaymentResponseHelper::OnInstrumentDetailsReady(
+    const std::string& method_name,
+    const std::string& stringified_details) {
+  mojom::PaymentResponsePtr payment_response = mojom::PaymentResponse::New();
+
+  // Make sure that we return the method name that the merchant specified for
+  // this instrument: cards can be either specified through their name (e.g.,
+  // "visa") or through basic-card's supportedNetworks.
+  payment_response->method_name =
+      spec_->IsMethodSupportedThroughBasicCard(method_name)
+          ? kBasicCardMethodName
+          : method_name;
+  payment_response->stringified_details = stringified_details;
+
+  // Shipping Address section
+  if (spec_->request_shipping()) {
+    DCHECK(selected_shipping_profile_);
+    payment_response->shipping_address =
+        GetMojomPaymentAddressFromAutofillProfile(selected_shipping_profile_,
+                                                  app_locale_);
+
+    DCHECK(spec_->selected_shipping_option());
+    payment_response->shipping_option = spec_->selected_shipping_option()->id;
+  }
+
+  // Contact Details section.
+  if (spec_->request_payer_name()) {
+    DCHECK(selected_contact_profile_);
+    payment_response->payer_name =
+        base::UTF16ToUTF8(selected_contact_profile_->GetInfo(
+            autofill::AutofillType(autofill::NAME_FULL), app_locale_));
+  }
+  if (spec_->request_payer_email()) {
+    DCHECK(selected_contact_profile_);
+    payment_response->payer_email = base::UTF16ToUTF8(
+        selected_contact_profile_->GetRawInfo(autofill::EMAIL_ADDRESS));
+  }
+  if (spec_->request_payer_phone()) {
+    DCHECK(selected_contact_profile_);
+    // TODO(crbug.com/705945): Format phone number according to spec.
+    payment_response->payer_phone =
+        base::UTF16ToUTF8(selected_contact_profile_->GetRawInfo(
+            autofill::PHONE_HOME_WHOLE_NUMBER));
+  }
+
+  delegate_->OnPaymentResponseReady(std::move(payment_response));
+}
+
 }  // namespace payments
diff --git a/components/payments/content/payment_response_helper.h b/components/payments/content/payment_response_helper.h
index 0912302a..c8903e22 100644
--- a/components/payments/content/payment_response_helper.h
+++ b/components/payments/content/payment_response_helper.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "components/payments/content/payment_request.mojom.h"
+#include "components/payments/core/payment_instrument.h"
 
 namespace autofill {
 class AutofillProfile;
@@ -14,21 +15,54 @@
 
 namespace payments {
 
-// TODO(sebsg): Accept PaymentInstrument and handle generating the payment
-// aspect of the PaymentResponse in this class.
+class PaymentRequestSpec;
+
 // TODO(sebsg): Asynchronously normalize the billing and shipping addresses
 // before adding them to the PaymentResponse.
 // A helper class to facilitate the creation of the PaymentResponse.
-class PaymentResponseHelper {
+class PaymentResponseHelper : public PaymentInstrument::Delegate {
  public:
-  PaymentResponseHelper();
-  ~PaymentResponseHelper();
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
 
+    virtual void OnPaymentResponseReady(
+        mojom::PaymentResponsePtr payment_response) = 0;
+  };
+
+  // The spec, selected_instrument and delegate cannot be null.
+  PaymentResponseHelper(const std::string& app_locale,
+                        PaymentRequestSpec* spec,
+                        PaymentInstrument* selected_instrument,
+                        autofill::AutofillProfile* selected_shipping_profile,
+                        autofill::AutofillProfile* selected_contact_profile,
+                        Delegate* delegate);
+  ~PaymentResponseHelper() override;
+
+  // Returns a new mojo PaymentAddress based on the specified
+  // |profile| and |app_locale|.
   static mojom::PaymentAddressPtr GetMojomPaymentAddressFromAutofillProfile(
       const autofill::AutofillProfile* const profile,
       const std::string& app_locale);
 
+  // PaymentInstrument::Delegate
+  void OnInstrumentDetailsReady(
+      const std::string& method_name,
+      const std::string& stringified_details) override;
+  void OnInstrumentDetailsError() override {}
+
  private:
+  const std::string& app_locale_;
+
+  // Not owned, cannot be null.
+  PaymentRequestSpec* spec_;
+  Delegate* delegate_;
+  PaymentInstrument* selected_instrument_;
+
+  // Not owned, can be null (dependent on the spec).
+  autofill::AutofillProfile* selected_shipping_profile_;
+  autofill::AutofillProfile* selected_contact_profile_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentResponseHelper);
 };
 
diff --git a/components/payments/content/payment_response_helper_unittest.cc b/components/payments/content/payment_response_helper_unittest.cc
new file mode 100644
index 0000000..d072121
--- /dev/null
+++ b/components/payments/content/payment_response_helper_unittest.cc
@@ -0,0 +1,269 @@
+// 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/payments/content/payment_response_helper.h"
+
+#include <utility>
+
+#include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/content/payment_request.mojom.h"
+#include "components/payments/content/payment_request_spec.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_request_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+
+class FakePaymentRequestDelegate : public PaymentRequestDelegate {
+ public:
+  FakePaymentRequestDelegate(
+      autofill::PersonalDataManager* personal_data_manager)
+      : personal_data_manager_(personal_data_manager), locale_("en-US") {}
+  void ShowDialog(PaymentRequest* request) override {}
+
+  void CloseDialog() override {}
+
+  void ShowErrorMessage() override {}
+
+  autofill::PersonalDataManager* GetPersonalDataManager() override {
+    return personal_data_manager_;
+  }
+
+  const std::string& GetApplicationLocale() const override { return locale_; }
+
+  bool IsIncognito() const override { return false; }
+
+  void DoFullCardRequest(
+      const autofill::CreditCard& credit_card,
+      base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+          result_delegate) override {
+    result_delegate->OnFullCardRequestSucceeded(credit_card,
+                                                base::ASCIIToUTF16("123"));
+  }
+
+ private:
+  autofill::PersonalDataManager* personal_data_manager_;
+  std::string locale_;
+  DISALLOW_COPY_AND_ASSIGN(FakePaymentRequestDelegate);
+};
+
+class PaymentResponseHelperTest : public testing::Test,
+                                  public PaymentResponseHelper::Delegate {
+ protected:
+  PaymentResponseHelperTest()
+      : payment_request_delegate_(&test_personal_data_manager_),
+        address_(autofill::test::GetFullProfile()),
+        billing_addresses_({&address_}) {
+    test_personal_data_manager_.AddTestingProfile(&address_);
+
+    // Setup the autofill payment instrument.
+    autofill::CreditCard visa_card = autofill::test::GetCreditCard();
+    visa_card.set_billing_address_id(address_.guid());
+    visa_card.set_use_count(5u);
+    autofill_instrument_ = base::MakeUnique<AutofillPaymentInstrument>(
+        "visa", visa_card, billing_addresses_, "en-US",
+        &payment_request_delegate_);
+  }
+  ~PaymentResponseHelperTest() override {}
+
+  // PaymentRequestState::Delegate:
+  void OnPaymentResponseReady(mojom::PaymentResponsePtr response) override {
+    payment_response_ = std::move(response);
+  };
+
+  // Convenience method to create a PaymentRequestSpec with specified |details|
+  // and |method_data|.
+  void RecreateSpecWithOptionsAndDetails(
+      mojom::PaymentOptionsPtr options,
+      mojom::PaymentDetailsPtr details,
+      std::vector<mojom::PaymentMethodDataPtr> method_data) {
+    // The spec will be based on the |options| and |details| passed in.
+    spec_ = base::MakeUnique<PaymentRequestSpec>(
+        std::move(options), std::move(details), std::move(method_data), nullptr,
+        "en-US");
+  }
+
+  // Convenience method to create a PaymentRequestSpec with default details
+  // (one shipping option) and method data (only supports visa).
+  void RecreateSpecWithOptions(mojom::PaymentOptionsPtr options) {
+    // Create dummy PaymentDetails with a single shipping option.
+    std::vector<mojom::PaymentShippingOptionPtr> shipping_options;
+    mojom::PaymentShippingOptionPtr option =
+        mojom::PaymentShippingOption::New();
+    option->id = "option:1";
+    shipping_options.push_back(std::move(option));
+    mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
+    details->shipping_options = std::move(shipping_options);
+
+    RecreateSpecWithOptionsAndDetails(std::move(options), std::move(details),
+                                      GetMethodDataForVisa());
+  }
+
+  // Convenience method that returns MethodData that supports Visa.
+  std::vector<mojom::PaymentMethodDataPtr> GetMethodDataForVisa() {
+    std::vector<mojom::PaymentMethodDataPtr> method_data;
+    mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
+    entry->supported_methods.push_back("visa");
+    method_data.push_back(std::move(entry));
+    return method_data;
+  }
+
+  PaymentRequestSpec* spec() { return spec_.get(); }
+  const mojom::PaymentResponsePtr& response() { return payment_response_; }
+  autofill::AutofillProfile* test_address() { return &address_; }
+  PaymentInstrument* test_instrument() { return autofill_instrument_.get(); }
+
+ private:
+  std::unique_ptr<PaymentRequestSpec> spec_;
+  mojom::PaymentResponsePtr payment_response_;
+  autofill::TestPersonalDataManager test_personal_data_manager_;
+  FakePaymentRequestDelegate payment_request_delegate_;
+
+  // Test data.
+  autofill::AutofillProfile address_;
+  const std::vector<autofill::AutofillProfile*> billing_addresses_;
+  std::unique_ptr<AutofillPaymentInstrument> autofill_instrument_;
+};
+
+// Test generating a PaymentResponse.
+TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_SupportedMethod) {
+  // Default options (no shipping, no contact info).
+  RecreateSpecWithOptions(mojom::PaymentOptions::New());
+
+  // TODO(mathp): Currently synchronous, when async will need a RunLoop.
+  // "visa" is specified directly in the supportedMethods so it is returned
+  // as the method name.
+  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+                               test_address(), test_address(), this);
+  EXPECT_EQ("visa", response()->method_name);
+  EXPECT_EQ(
+      "{\"billingAddress\":"
+      "{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
+      "\"city\":\"Elysium\","
+      "\"country\":\"US\","
+      "\"organization\":\"Underworld\","
+      "\"phone\":\"16502111111\","
+      "\"postalCode\":\"91111\","
+      "\"recipient\":\"John H. Doe\","
+      "\"region\":\"CA\"},"
+      "\"cardNumber\":\"4111111111111111\","
+      "\"cardSecurityCode\":\"123\","
+      "\"cardholderName\":\"Test User\","
+      "\"expiryMonth\":\"11\","
+      "\"expiryYear\":\"2022\"}",
+      response()->stringified_details);
+}
+
+// Test generating a PaymentResponse when the method is specified through
+// "basic-card".
+TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_BasicCard) {
+  // The method data supports visa through basic-card.
+  mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
+  entry->supported_methods.push_back("basic-card");
+  entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
+  std::vector<mojom::PaymentMethodDataPtr> method_data;
+  method_data.push_back(std::move(entry));
+  RecreateSpecWithOptionsAndDetails(mojom::PaymentOptions::New(),
+                                    mojom::PaymentDetails::New(),
+                                    std::move(method_data));
+
+  // TODO(mathp): Currently synchronous, when async will need a RunLoop.
+  // "basic-card" is specified so it is returned as the method name.
+  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+                               test_address(), test_address(), this);
+  EXPECT_EQ("basic-card", response()->method_name);
+  EXPECT_EQ(
+      "{\"billingAddress\":"
+      "{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
+      "\"city\":\"Elysium\","
+      "\"country\":\"US\","
+      "\"organization\":\"Underworld\","
+      "\"phone\":\"16502111111\","
+      "\"postalCode\":\"91111\","
+      "\"recipient\":\"John H. Doe\","
+      "\"region\":\"CA\"},"
+      "\"cardNumber\":\"4111111111111111\","
+      "\"cardSecurityCode\":\"123\","
+      "\"cardholderName\":\"Test User\","
+      "\"expiryMonth\":\"11\","
+      "\"expiryYear\":\"2022\"}",
+      response()->stringified_details);
+}
+
+// Tests the the generated PaymentResponse has the correct values for the
+// shipping address.
+TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_ShippingAddress) {
+  // Setup so that a shipping address is requested.
+  std::vector<mojom::PaymentShippingOptionPtr> shipping_options;
+  mojom::PaymentShippingOptionPtr option = mojom::PaymentShippingOption::New();
+  option->id = "option:1";
+  option->selected = true;
+  shipping_options.push_back(std::move(option));
+  mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
+  details->shipping_options = std::move(shipping_options);
+  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
+  options->request_shipping = true;
+  RecreateSpecWithOptionsAndDetails(std::move(options), std::move(details),
+                                    GetMethodDataForVisa());
+
+  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+                               test_address(), test_address(), this);
+
+  // Check that all the expected values were set.
+  EXPECT_EQ("US", response()->shipping_address->country);
+  EXPECT_EQ("666 Erebus St.", response()->shipping_address->address_line[0]);
+  EXPECT_EQ("Apt 8", response()->shipping_address->address_line[1]);
+  EXPECT_EQ("CA", response()->shipping_address->region);
+  EXPECT_EQ("Elysium", response()->shipping_address->city);
+  EXPECT_EQ("", response()->shipping_address->dependent_locality);
+  EXPECT_EQ("91111", response()->shipping_address->postal_code);
+  EXPECT_EQ("", response()->shipping_address->sorting_code);
+  EXPECT_EQ("", response()->shipping_address->language_code);
+  EXPECT_EQ("Underworld", response()->shipping_address->organization);
+  EXPECT_EQ("John H. Doe", response()->shipping_address->recipient);
+  EXPECT_EQ("16502111111", response()->shipping_address->phone);
+}
+
+// Tests the the generated PaymentResponse has the correct values for the
+// contact details when all values are requested.
+TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_ContactDetails_All) {
+  // Request all contact detail values.
+  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
+  options->request_payer_name = true;
+  options->request_payer_phone = true;
+  options->request_payer_email = true;
+  RecreateSpecWithOptions(std::move(options));
+
+  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+                               test_address(), test_address(), this);
+
+  // Check that all the expected values were set.
+  EXPECT_EQ("John H. Doe", response()->payer_name.value());
+  EXPECT_EQ("16502111111", response()->payer_phone.value());
+  EXPECT_EQ("johndoe@hades.com", response()->payer_email.value());
+}
+
+// Tests the the generated PaymentResponse has the correct values for the
+// contact details when all values are requested.
+TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_ContactDetails_Some) {
+  // Request one contact detail value.
+  mojom::PaymentOptionsPtr options = mojom::PaymentOptions::New();
+  options->request_payer_name = true;
+  RecreateSpecWithOptions(std::move(options));
+
+  PaymentResponseHelper helper("en-US", spec(), test_instrument(),
+                               test_address(), test_address(), this);
+
+  // Check that the name was set, but not the other values.
+  EXPECT_EQ("John H. Doe", response()->payer_name.value());
+  EXPECT_FALSE(response()->payer_phone.has_value());
+  EXPECT_FALSE(response()->payer_email.has_value());
+}
+
+}  // namespace payments
diff --git a/components/payments_strings.grdp b/components/payments_strings.grdp
index bc1ba8f..b1e6220 100644
--- a/components/payments_strings.grdp
+++ b/components/payments_strings.grdp
@@ -10,11 +10,11 @@
   <message name="IDS_PAYMENTS_SAVE_CARD_TO_DEVICE_CHECKBOX" desc="The label for the checkbox that enables the user to save a credit card to their device, for example, on their phone." formatter_data="android_java">
     Save this card to this device
   </message>
-  <message name="IDS_PAYMENTS_ACCEPTED_CARDS_LABEL" desc="The title for the section that displays the credit card types that the merchant accepts." formatter_data="android_java">
-    Cards accepted
+  <message name="IDS_PAYMENTS_ACCEPTED_CARDS_LABEL" desc="The title for the section that displays the credit card types that the merchant accepts. Below the title, we show a row of icons indicating the accepted cards (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+    Accepted cards
   </message>
   <message name="IDS_PAYMENTS_METHOD_OF_PAYMENT_LABEL" desc="The title for the section that lets the user select the method of payment." formatter_data="android_java">
-    Payment
+    Payment method
   </message>
   <message name="IDS_PAYMENTS_CONTACT_DETAILS_LABEL" desc="The title for the section that lets the user select how they can be contacted." formatter_data="android_java">
     Contact info
@@ -37,14 +37,14 @@
   <message name="IDS_PAYMENTS_ADD_VALID_CARD_NUMBER" desc="The title of the dialog for user to add valid payment card number." formatter_data="android_java">
     Add valid card number
   </message>
-  <message name="IDS_PAYMENTS_ADD_MORE_INFORMATION" desc="The title of the dialog for user to add more information to payment card or shipping address or contact info." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_ADD_MORE_INFORMATION" desc="The title in the Android app title bar for user to add more information to payment card or shipping address or contact info." formatter_data="android_java">
     Add more information
   </message>
   <message name="IDS_PAYMENTS_EDIT_CARD" desc="The title of the dialog for user to edit payment card." formatter_data="android_java">
     Edit card
   </message>
   <message name="IDS_PAYMENTS_CREDIT_CARD_EXPIRATION_DATE_ABBR" desc="Abbreviated label for credit card expiration date. [CHAR-LIMIT=32]" formatter_data="android_java">
-    Exp: <ph name="EXPIRATION_MONTH">%1$s<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">%2$s<ex>17</ex></ph>
+    Expires <ph name="EXPIRATION_MONTH">%1$s<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">%2$s<ex>17</ex></ph>
   </message>
   <message name="IDS_PAYMENTS_ADD_PHONE_NUMBER" desc="The title of the dialog for user to add phone number to the shipping address or contact info. This phone number can be used, for example, if there's a problem with shipping a package to its destination." formatter_data="android_java">
     Add phone number
@@ -77,7 +77,7 @@
     Processing
   </message>
   <message name="IDS_PAYMENTS_ERROR_MESSAGE_DIALOG_TITLE" desc="The title of the dialog that informs the user that there is error in verifying and charging the payment.">
-    Payment Not Completed
+    Payment not completed
   </message>
   <message name="IDS_PAYMENTS_ERROR_MESSAGE" desc="The text that informs the user that there is error in verifying and charging the payment." formatter_data="android_java">
     There was an error processing your order. Please try again.
@@ -101,10 +101,10 @@
     You can manage cards and addresses in <ph name="BEGIN_LINK">BEGIN_LINK</ph>Settings<ph name="END_LINK">END_LINK</ph>.
   </message>
   <message name="IDS_PAYMENTS_CARD_AND_ADDRESS_SETTINGS_SIGNED_IN" desc="Label of the section containing the origin description and the link to go to the settings page for card and address options. This label is used when the user is signed in." formatter_data="android_java">
-    Card and address options are from your Google Account (<ph name="ACCOUNT_EMAIL">%1$s<ex>johndoe@gmail.com</ex></ph>) and Chrome. You can manage these in <ph name="BEGIN_LINK">BEGIN_LINK</ph>Settings<ph name="END_LINK">END_LINK</ph>.
+    Cards and addresses are from Chrome and your Google Account (<ph name="ACCOUNT_EMAIL">%1$s<ex>johndoe@gmail.com</ex></ph>). You can manage them in <ph name="BEGIN_LINK">BEGIN_LINK</ph>Settings<ph name="END_LINK">END_LINK</ph>.
   </message>
   <message name="IDS_PAYMENTS_CARD_AND_ADDRESS_SETTINGS_SIGNED_OUT" desc="Label of the section containing the origin description and the link to go to the settings page for card and address options. This label is used when the user is not signed in." formatter_data="android_java">
-    Card and address options are from Chrome. You can manage these in <ph name="BEGIN_LINK">BEGIN_LINK</ph>Settings<ph name="END_LINK">END_LINK</ph>.
+    Cards and addresses are from Chrome. You can manage them in <ph name="BEGIN_LINK">BEGIN_LINK</ph>Settings<ph name="END_LINK">END_LINK</ph>.
   </message>
 
   <!-- Credit Card form -->
@@ -113,7 +113,7 @@
       Card Number
     </message>
     <message name="IDS_PAYMENTS_NAME_ON_CARD" desc="Title of the field representing the full name of a credit card holder.">
-      Name on Card
+      Cardholder Name
     </message>
       <message name="IDS_PAYMENTS_EXP_MONTH" desc="Title of the field representing the expiration month of a credit card.">
       Expiration Month
@@ -128,43 +128,43 @@
 
   <!-- Validation -->
   <message name="IDS_PAYMENTS_VALIDATION_INVALID_NAME" desc="Message displayed to user when name validation fails.">
-    Invalid name
+    Enter a name
   </message>
   <message name="IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR" desc="Message displayed to user when the credit card expiration year is in an invalid format.">
-    Invalid expiration year
+    Enter a valid expiration year
   </message>
   <message name="IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH" desc="Message displayed to user when the credit card expiration month is in an invalid format.">
-    Invalid expiration month
+    Enter a valid expiration month
   </message>
   <message name="IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED" desc="Message displayed to user when the credit card is expired.">
-    The card is expired
+    This card is expired
   </message>
   <message name="IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE" desc="Message displayed to user when the credit card type (e.g visa, mastercard) is not supported for this transaction.">
-    Unsupported card type
+    This type of card isn’t supported
   </message>
   <message name="IDS_PAYMENTS_REQUIRED_FIELD_MESSAGE" desc="The text that informs the user that '*' character indicates an input field that is required. The '*' character should not be changed." formatter_data="android_java">
-    * indicates required field
+     * Field is required
   </message>
-  <message name="IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE" desc="The text that informs the user that an input field is required." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE" desc="The text that informs the user that an input field is required. “Required” is an adjective." formatter_data="android_java">
     Required field
   </message>
   <message name="IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE" desc="The text that informs the user that the phone number they have entered is not valid." formatter_data="android_java">
-    Invalid phone number
+    Enter a valid phone number
   </message>
   <message name="IDS_PAYMENTS_EMAIL_INVALID_VALIDATION_MESSAGE" desc="The text that informs the user that the email address they have entered is not valid." formatter_data="android_java">
-    Invalid email address
+    Enter a valid email address
   </message>
   <message name="IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE" desc="The text that informs the user that the credit card number they have entered is not valid." formatter_data="android_java">
-    Invalid card number
+    Enter a valid card number
   </message>
   <message name="IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE" desc="The text that informs the user that the credit card expiration date they have entered is not valid." formatter_data="android_java">
-    Invalid expiration date
+    Enter a valid expiration date
   </message>
   <message name="IDS_PAYMENTS_BILLING_ADDRESS_REQUIRED" desc="The label to indicate billing address is required for payment card." formatter_data="android_java">
     Billing address required
   </message>
-  <message name="IDS_PAYMENTS_NAME_ON_CARD_REQUIRED" desc="The label to indicate name on card is required for payment card." formatter_data="android_java">
-    Name on card required
+  <message name="IDS_PAYMENTS_NAME_ON_CARD_REQUIRED" desc="The label to indicate the cardholder name (the name of the owner or “holder” of the credit card)  must be entered." formatter_data="android_java">
+    Cardholder name required
   </message>
   <message name="IDS_PAYMENTS_MORE_INFORMATION_REQUIRED" desc="The label to indicate more information is required for payment card or shipping address or contact info." formatter_data="android_java">
     More information required
@@ -176,7 +176,7 @@
     Name required
   </message>
   <message name="IDS_PAYMENTS_INVALID_ADDRESS" desc="The label to indicate the shipping address is invalid. For example, missing state or city name." formatter_data="android_java">
-    Invalid address
+    Enter a valid address
   </message>
   <message name="IDS_PAYMENTS_EMAIL_REQUIRED" desc="The label to indicate email is required for the contact details. This email can be used to contact the payer." formatter_data="android_java">
     Email required
@@ -209,66 +209,66 @@
   </message>
 
   <!-- Shipping address in web payments API -->
-  <message name="IDS_PAYMENTS_SHIPPING_SUMMARY_LABEL" desc="The title for the section of shipping information. Shipping is typically used for packages." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_SHIPPING_SUMMARY_LABEL" desc="The title for the section of shipping information. Shipping is used for packages and things that are mailed. In American English, “shipping” is differentiated from “delivery”. “Delivery” is used, for example, for food delivery." formatter_data="android_java">
     Shipping
   </message>
   <message name="IDS_PAYMENTS_SHIPPING_ADDRESS_LABEL" desc="The title for the section that lets the user select the address where the product should be shipped. Shipping is typically used for packages." formatter_data="android_java">
     Shipping address
   </message>
   <message name="IDS_PAYMENTS_SHIPPING_OPTION_LABEL" desc="The title for the section that lets the user select how the product should be shipped. Shipping is typically used for packages." formatter_data="android_java">
-    Shipping option
+    Shipping method
   </message>
   <message name="IDS_PAYMENTS_SELECT_SHIPPING_ADDRESS_FOR_SHIPPING_METHODS" desc="Text implying that a user needs to pick a shipping address to see the shipping methods. Shipping is typically used for packages." formatter_data="android_java">
-    Select a shipping address to check shipping methods and requirements.
+    To see shipping methods and requirements, select an address
   </message>
-  <message name="IDS_PAYMENTS_UNSUPPORTED_SHIPPING_ADDRESS" desc="Text implying that a user needs to pick a different shipping address, because the currently selected address is not supported. Shipping is typically used for packages." formatter_data="android_java">
-    Unsupported shipping address. Select a different address.
+  <message name="IDS_PAYMENTS_UNSUPPORTED_SHIPPING_ADDRESS" desc="Text implying that a user needs to pick a different shipping address, because the currently selected address is not supported. “Ship” is typically used for packages and items that are sent through the mail. In American English, we differentiate “ship” from “deliver." formatter_data="android_java">
+    Can’t ship to this address. Select a different address.
   </message>
-  <message name="IDS_PAYMENTS_UNSUPPORTED_SHIPPING_OPTION" desc="Text implying that a user needs to pick a different shipping option, because the currently selected option is not supported. Shipping is typically used for packages." formatter_data="android_java">
-    That shipping option isn’t available. Try a different option.
+  <message name="IDS_PAYMENTS_UNSUPPORTED_SHIPPING_OPTION" desc="Text implying that a user needs to pick a different shipping option, because the currently selected option is not supported. “Ship” is typically used for packages and items that are sent through the mail. In American English, we differentiate “ship” from deliver." formatter_data="android_java">
+    This shipping method isn’t available. Try a different method.
   </message>
 
   <!-- Delivery address in web payments API -->
   <message name="IDS_PAYMENTS_DELIVERY_SUMMARY_LABEL" desc="The title for the section of delivery information. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
     Delivery
   </message>
-  <message name="IDS_PAYMENTS_DELIVERY_ADDRESS_LABEL" desc="The title for the section that lets the user select the address where the product should be delivered. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_DELIVERY_ADDRESS_LABEL" desc="The title for the section that lets the user select the address where the product should be delivered. In English, “delivery” is differentiated from “shipping.”  For example, “delivery” might be used for meals or meal items or items that need to be immediately sent to a recipient." formatter_data="android_java">
     Delivery address
   </message>
   <message name="IDS_PAYMENTS_DELIVERY_OPTION_LABEL" desc="The title for the section that lets the user select how the product should be delivered. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
-    Delivery option
+    Delivery method
   </message>
   <message name="IDS_PAYMENTS_SELECT_DELIVERY_ADDRESS_FOR_DELIVERY_METHODS" desc="Text implying that a user needs to pick a delivery address to see the delivery methods. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
-    Select a delivery address to check delivery methods and requirements.
+    To see delivery methods and requirements, select an address
   </message>
   <message name="IDS_PAYMENTS_UNSUPPORTED_DELIVERY_ADDRESS" desc="Text implying that a user needs to pick a different delivery address, because the currently selected address is not supported. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
-    Unsupported delivery address. Select a different address.
+    Can’t deliver to this address. Select a different address.
   </message>
   <message name="IDS_PAYMENTS_UNSUPPORTED_DELIVERY_OPTION" desc="Text implying that a user needs to pick a different delivery option, because the currently selected option is not supported. Delivery is commonly faster than shipping. For example, it might be used for food delivery." formatter_data="android_java">
-    That delivery option isn’t available. Try a different option.
+    This delivery method isn’t available. Try a different method.
   </message>
 
   <!-- Pickup address in web payments API -->
-  <message name="IDS_PAYMENTS_PICKUP_SUMMARY_LABEL" desc="The title for the section of pickup information. For example, this could be the address for laundry pickup." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_PICKUP_SUMMARY_LABEL" desc="The title for the section of information needed for a service to pick up items from a customer. For example, this could be the address for laundry pickup. “Pickup” is a noun." formatter_data="android_java">
     Pickup
   </message>
-  <message name="IDS_PAYMENTS_PICKUP_ADDRESS_LABEL" desc="The title for the section that lets the user select the address where their item should be picked up. For example, this item could be laundry to be cleaned." formatter_data="android_java">
+  <message name="IDS_PAYMENTS_PICKUP_ADDRESS_LABEL" desc="The title for the section that lets the user select the address where a service item should be picked up. For example, the pickup address is where a laundry service picks up a customer’s items to be laundered. “Pickup” functions as an adjective." formatter_data="android_java">
     Pickup address
   </message>
-  <message name="IDS_PAYMENTS_PICKUP_OPTION_LABEL" desc="The title for the section that lets the user select how their item should be picked up. This item can be laundry to be cleaned, for example." formatter_data="android_java">
-    Pickup option
+  <message name="IDS_PAYMENTS_PICKUP_OPTION_LABEL" desc="The title for the section that lets the user select how their service item should be picked up. This item can be laundry to be cleaned, for example. “Pickup” functions as an adjective." formatter_data="android_java">
+    Pickup method
   </message>
-  <message name="IDS_PAYMENTS_SELECT_PICKUP_ADDRESS_FOR_PICKUP_METHODS" desc="Text implying that a user needs to choose a pickup address to see the pickup methods. For example, this could be the address for laundry pickup." formatter_data="android_java">
-    Select a pickup address to check pickup methods and requirements.
+  <message name="IDS_PAYMENTS_SELECT_PICKUP_ADDRESS_FOR_PICKUP_METHODS" desc="Text implying that a user needs to choose a pickup address to see the pickup methods. For example, this could be the address for laundry pickup. “Pickup” functions as an adjective." formatter_data="android_java">
+    To see pickup methods and requirements, select an address
   </message>
-  <message name="IDS_PAYMENTS_UNSUPPORTED_PICKUP_ADDRESS" desc="Text implying that a user needs to choose a different pickup address, because the currently selected address is not supported. This address can be used, for example, for laundry pickup." formatter_data="android_java">
-    Unsupported pickup address. Select a different address.
+  <message name="IDS_PAYMENTS_UNSUPPORTED_PICKUP_ADDRESS" desc="Text implying that a user needs to choose a different pickup address, because the service can’t pick up items from that address. This address can be used, for example, for laundry pickup. Note that here, “pick up” is a verb." formatter_data="android_java">
+    Can’t pick up from this address. Select a different address.
   </message>
   <message name="IDS_PAYMENTS_UNSUPPORTED_PICKUP_OPTION" desc="Text implying that a user needs to choose a different pickup option, because the currently selected option is not supported. This option can be used, for example, for laundry pickup." formatter_data="android_java">
-    That pickup option isn’t available. Try a different option.
+    This pickup method isn’t available. Try a different method.
   </message>
   <message name="IDS_PAYMENTS_ANDROID_APP_ERROR" desc="Error message that is shown when an Android payment application fails to start." formatter_data="android_java">
-    Unable to launch payment app.
+    Can’t open payment app
   </message>
 
   <message name="IDS_UTILITY_PROCESS_PAYMENT_MANIFEST_PARSER_NAME" desc="The name of the utility process used for parsing payment manifest files.">
diff --git a/components/policy/core/browser/configuration_policy_handler.cc b/components/policy/core/browser/configuration_policy_handler.cc
index 17de0cd..e591f99 100644
--- a/components/policy/core/browser/configuration_policy_handler.cc
+++ b/components/policy/core/browser/configuration_policy_handler.cc
@@ -184,7 +184,7 @@
 
   for (auto entry = list_value->begin(); entry != list_value->end(); ++entry) {
     std::string entry_value;
-    if (!(*entry)->GetAsString(&entry_value)) {
+    if (!entry->GetAsString(&entry_value)) {
       if (errors) {
         errors->AddError(policy_name(), entry - list_value->begin(),
                          IDS_POLICY_TYPE_ERROR,
diff --git a/components/policy/core/browser/url_blacklist_policy_handler.cc b/components/policy/core/browser/url_blacklist_policy_handler.cc
index 1546190..f74f43d 100644
--- a/components/policy/core/browser/url_blacklist_policy_handler.cc
+++ b/components/policy/core/browser/url_blacklist_policy_handler.cc
@@ -60,7 +60,7 @@
   if (disabled_schemes) {
     for (const auto& entry : *disabled_schemes) {
       std::string entry_value;
-      if (entry->GetAsString(&entry_value)) {
+      if (entry.GetAsString(&entry_value)) {
         entry_value.append("://*");
         merged_url_blacklist->AppendString(entry_value);
       }
@@ -69,8 +69,8 @@
 
   if (url_blacklist) {
     for (const auto& entry : *url_blacklist) {
-      if (entry->IsType(base::Value::Type::STRING))
-        merged_url_blacklist->Append(entry->CreateDeepCopy());
+      if (entry.IsType(base::Value::Type::STRING))
+        merged_url_blacklist->Append(entry.CreateDeepCopy());
     }
   }
 
diff --git a/components/policy/core/common/policy_loader_win.cc b/components/policy/core/common/policy_loader_win.cc
index d86ae5d..b5ff23fa 100644
--- a/components/policy/core/common/policy_loader_win.cc
+++ b/components/policy/core/common/policy_loader_win.cc
@@ -113,7 +113,7 @@
     std::unique_ptr<base::ListValue> filtered_values(new base::ListValue);
     for (const auto& list_entry : *policy_list_value) {
       std::string entry;
-      if (!list_entry->GetAsString(&entry))
+      if (!list_entry.GetAsString(&entry))
         continue;
       size_t pos = entry.find(';');
       if (pos == std::string::npos)
diff --git a/components/policy/core/common/policy_loader_win_unittest.cc b/components/policy/core/common/policy_loader_win_unittest.cc
index db72ac73..be4652d 100644
--- a/components/policy/core/common/policy_loader_win_unittest.cc
+++ b/components/policy/core/common/policy_loader_win_unittest.cc
@@ -398,7 +398,7 @@
        element != policy_value->end();
        ++element) {
     std::string element_value;
-    if (!(*element)->GetAsString(&element_value))
+    if (!element->GetAsString(&element_value))
       continue;
     std::string name(base::IntToString(index++));
     key.WriteValue(UTF8ToUTF16(name).c_str(),
diff --git a/components/policy/core/common/policy_test_utils.cc b/components/policy/core/common/policy_test_utils.cc
index 7f4e5973..fcf89f9 100644
--- a/components/policy/core/common/policy_test_utils.cc
+++ b/components/policy/core/common/policy_test_utils.cc
@@ -126,7 +126,7 @@
           // CFArrayAppendValue() retains |cf_value|, so make sure the reference
           // created by ValueToProperty() is released.
           base::ScopedCFTypeRef<CFPropertyListRef> cf_value(
-              ValueToProperty(*entry));
+              ValueToProperty(entry));
           if (cf_value)
             CFArrayAppendValue(array, cf_value);
         }
diff --git a/components/policy/core/common/registry_dict.cc b/components/policy/core/common/registry_dict.cc
index f421904..124a2cd11 100644
--- a/components/policy/core/common/registry_dict.cc
+++ b/components/policy/core/common/registry_dict.cc
@@ -62,7 +62,7 @@
       for (base::ListValue::const_iterator entry(list->begin());
            entry != list->end(); ++entry) {
         std::unique_ptr<base::Value> converted =
-            ConvertValue(**entry, schema.GetItems());
+            ConvertValue(*entry, schema.GetItems());
         if (converted)
           result->Append(std::move(converted));
       }
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
index 330848c..31dae8e6 100644
--- a/components/policy/core/common/schema.cc
+++ b/components/policy/core/common/schema.cc
@@ -619,7 +619,7 @@
     int value;
     for (base::ListValue::const_iterator it = possible_values->begin();
          it != possible_values->end(); ++it) {
-      if (!(*it)->GetAsInteger(&value)) {
+      if (!it->GetAsInteger(&value)) {
         *error = "Invalid enumeration member type";
         return false;
       }
@@ -631,7 +631,7 @@
     std::string value;
     for (base::ListValue::const_iterator it = possible_values->begin();
          it != possible_values->end(); ++it) {
-      if (!(*it)->GetAsString(&value)) {
+      if (!it->GetAsString(&value)) {
         *error = "Invalid enumeration member type";
         return false;
       }
@@ -826,10 +826,7 @@
   } else if (value.GetAsList(&list)) {
     for (base::ListValue::const_iterator it = list->begin(); it != list->end();
          ++it) {
-      if (!*it ||
-          !GetItems().Validate(**it,
-                               StrategyForNextLevel(strategy),
-                               error_path,
+      if (!GetItems().Validate(*it, StrategyForNextLevel(strategy), error_path,
                                error)) {
         // Invalid list item was detected.
         AddListIndexPrefixToPath(it - list->begin(), error_path);
diff --git a/components/prefs/pref_member.cc b/components/prefs/pref_member.cc
index 8ed13ee5..f0be267 100644
--- a/components/prefs/pref_member.cc
+++ b/components/prefs/pref_member.cc
@@ -139,7 +139,7 @@
   for (base::ListValue::const_iterator it = list->begin();
        it != list->end(); ++it) {
     std::string string_value;
-    if (!(*it)->GetAsString(&string_value))
+    if (!it->GetAsString(&string_value))
       return false;
 
     local_vector.push_back(string_value);
diff --git a/components/reading_list/core/proto/reading_list.proto b/components/reading_list/core/proto/reading_list.proto
index 04a3e9463..9ca16de0 100644
--- a/components/reading_list/core/proto/reading_list.proto
+++ b/components/reading_list/core/proto/reading_list.proto
@@ -44,4 +44,10 @@
   optional string backoff = 10;
   optional int64 distillation_time_us = 14;
   optional int64 distillation_size = 15;
+  optional ReadingListContentSuggestionsExtra content_suggestions_extra = 16;
+}
+
+// Additional data used by the ContentSuggestions.
+message ReadingListContentSuggestionsExtra {
+  optional bool dismissed = 1;
 }
diff --git a/components/reading_list/core/reading_list_entry.cc b/components/reading_list/core/reading_list_entry.cc
index 784f76b7..33d97a2 100644
--- a/components/reading_list/core/reading_list_entry.cc
+++ b/components/reading_list/core/reading_list_entry.cc
@@ -68,7 +68,8 @@
                        0,
                        0,
                        0,
-                       std::move(backoff)) {}
+                       std::move(backoff),
+                       reading_list::ContentSuggestionsExtra()) {}
 
 ReadingListEntry::ReadingListEntry(
     const GURL& url,
@@ -84,7 +85,8 @@
     int64_t distillation_time,
     int64_t distillation_size,
     int failed_download_counter,
-    std::unique_ptr<net::BackoffEntry> backoff)
+    std::unique_ptr<net::BackoffEntry> backoff,
+    const reading_list::ContentSuggestionsExtra& content_suggestions_extra)
     : url_(url),
       title_(title),
       state_(state),
@@ -97,7 +99,8 @@
       update_time_us_(update_time),
       update_title_time_us_(update_title_time),
       distillation_time_us_(distillation_time),
-      distillation_size_(distillation_size) {
+      distillation_size_(distillation_size),
+      content_suggestions_extra_(content_suggestions_extra) {
   if (backoff) {
     backoff_ = std::move(backoff);
   } else {
@@ -124,7 +127,8 @@
       update_time_us_(std::move(entry.update_time_us_)),
       update_title_time_us_(std::move(entry.update_title_time_us_)),
       distillation_time_us_(std::move(entry.distillation_time_us_)),
-      distillation_size_(std::move(entry.distillation_size_)) {}
+      distillation_size_(std::move(entry.distillation_size_)),
+      content_suggestions_extra_(std::move(entry.content_suggestions_extra_)) {}
 
 ReadingListEntry::~ReadingListEntry() {}
 
@@ -179,6 +183,7 @@
   update_title_time_us_ = std::move(other.update_title_time_us_);
   distillation_time_us_ = std::move(other.distillation_time_us_);
   distillation_size_ = std::move(other.distillation_size_);
+  content_suggestions_extra_ = std::move(other.content_suggestions_extra_);
   return *this;
 }
 
@@ -216,6 +221,16 @@
   return state_ != UNSEEN;
 }
 
+const reading_list::ContentSuggestionsExtra*
+ReadingListEntry::ContentSuggestionsExtra() const {
+  return &content_suggestions_extra_;
+}
+
+void ReadingListEntry::SetContentSuggestionsExtra(
+    const reading_list::ContentSuggestionsExtra& extra) {
+  content_suggestions_extra_ = extra;
+}
+
 void ReadingListEntry::SetDistilledInfo(const base::FilePath& path,
                                         const GURL& distilled_url,
                                         int64_t distilation_size,
@@ -387,11 +402,20 @@
     }
   }
 
+  reading_list::ContentSuggestionsExtra content_suggestions_extra;
+  if (pb_entry.has_content_suggestions_extra()) {
+    const reading_list::ReadingListContentSuggestionsExtra& pb_extra =
+        pb_entry.content_suggestions_extra();
+    if (pb_extra.has_dismissed()) {
+      content_suggestions_extra.dismissed = pb_extra.dismissed();
+    }
+  }
+
   return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
       url, title, state, creation_time_us, first_read_time_us, update_time_us,
       update_title_time_us, distillation_state, distilled_path, distilled_url,
       distillation_time_us, distillation_size, failed_download_counter,
-      std::move(backoff)));
+      std::move(backoff), content_suggestions_extra));
 }
 
 // static
@@ -452,8 +476,8 @@
 
   return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
       url, title, state, creation_time_us, first_read_time_us, update_time_us,
-      update_title_time_us, WAITING, base::FilePath(), GURL(), 0, 0, 0,
-      nullptr));
+      update_title_time_us, WAITING, base::FilePath(), GURL(), 0, 0, 0, nullptr,
+      reading_list::ContentSuggestionsExtra()));
 }
 
 void ReadingListEntry::MergeWithEntry(const ReadingListEntry& other) {
@@ -579,6 +603,11 @@
     serializer.Serialize(*backoff);
     pb_entry->set_backoff(output);
   }
+
+  reading_list::ReadingListContentSuggestionsExtra* pb_extra =
+      pb_entry->mutable_content_suggestions_extra();
+  pb_extra->set_dismissed(content_suggestions_extra_.dismissed);
+
   return pb_entry;
 }
 
diff --git a/components/reading_list/core/reading_list_entry.h b/components/reading_list/core/reading_list_entry.h
index 042cf05c..878278b 100644
--- a/components/reading_list/core/reading_list_entry.h
+++ b/components/reading_list/core/reading_list_entry.h
@@ -22,6 +22,13 @@
 // |ADDED_VIA_EXTENSION| is when the entry was added via the share extension.
 // |ADDED_VIA_SYNC| is when the entry was added with sync.
 enum EntrySource { ADDED_VIA_CURRENT_APP, ADDED_VIA_EXTENSION, ADDED_VIA_SYNC };
+
+// Contains additional data used by the ContentSuggestions.
+struct ContentSuggestionsExtra {
+  // Whether the Reading List Entry has been dismissed in the Content
+  // Suggestions.
+  bool dismissed = false;
+};
 }
 
 namespace sync_pb {
@@ -101,6 +108,9 @@
   // Returns if an entry has ever been seen.
   bool HasBeenSeen() const;
 
+  // Extra information about this entry for the Content Suggestions.
+  const reading_list::ContentSuggestionsExtra* ContentSuggestionsExtra() const;
+
   // The last update time of the entry. This value may be used to sort the
   // entries. The value is in microseconds since Jan 1st 1970.
   int64_t UpdateTime() const;
@@ -172,23 +182,28 @@
   // If |first_read_time_us_| is 0 and read is READ, sets |first_read_time_us_|
   // to |now|.
   void SetRead(bool read, const base::Time& now);
+  // Sets extra information about this entry used by Content Suggestions.
+  void SetContentSuggestionsExtra(
+      const reading_list::ContentSuggestionsExtra& extra);
 
  private:
   enum State { UNSEEN, UNREAD, READ };
-  ReadingListEntry(const GURL& url,
-                   const std::string& title,
-                   State state,
-                   int64_t creation_time,
-                   int64_t first_read_time,
-                   int64_t update_time,
-                   int64_t update_title_time,
-                   ReadingListEntry::DistillationState distilled_state,
-                   const base::FilePath& distilled_path,
-                   const GURL& distilled_url,
-                   int64_t distillation_time,
-                   int64_t distillation_size,
-                   int failed_download_counter,
-                   std::unique_ptr<net::BackoffEntry> backoff);
+  ReadingListEntry(
+      const GURL& url,
+      const std::string& title,
+      State state,
+      int64_t creation_time,
+      int64_t first_read_time,
+      int64_t update_time,
+      int64_t update_title_time,
+      ReadingListEntry::DistillationState distilled_state,
+      const base::FilePath& distilled_path,
+      const GURL& distilled_url,
+      int64_t distillation_time,
+      int64_t distillation_size,
+      int failed_download_counter,
+      std::unique_ptr<net::BackoffEntry> backoff,
+      const reading_list::ContentSuggestionsExtra& content_suggestions_extra);
   GURL url_;
   std::string title_;
   State state_;
@@ -209,6 +224,8 @@
   int64_t distillation_time_us_;
   int64_t distillation_size_;
 
+  reading_list::ContentSuggestionsExtra content_suggestions_extra_;
+
   DISALLOW_COPY_AND_ASSIGN(ReadingListEntry);
 };
 
diff --git a/components/reading_list/core/reading_list_entry_unittest.cc b/components/reading_list/core/reading_list_entry_unittest.cc
index 23bc82f..6a0e8c80 100644
--- a/components/reading_list/core/reading_list_entry_unittest.cc
+++ b/components/reading_list/core/reading_list_entry_unittest.cc
@@ -318,6 +318,7 @@
   EXPECT_EQ(pb_entry->distilled_path(), "");
   EXPECT_EQ(pb_entry->failed_download_counter(), 0);
   EXPECT_NE(pb_entry->backoff(), "");
+  EXPECT_FALSE(pb_entry->content_suggestions_extra().dismissed());
 
   entry.SetDistilledState(ReadingListEntry::WILL_RETRY);
   std::unique_ptr<reading_list::ReadingListLocal> will_retry_pb_entry(
@@ -334,6 +335,11 @@
 
   entry.SetRead(true, base::Time::FromTimeT(20));
   entry.MarkEntryUpdated(base::Time::FromTimeT(30));
+
+  reading_list::ContentSuggestionsExtra extra;
+  extra.dismissed = true;
+  entry.SetContentSuggestionsExtra(extra);
+
   EXPECT_NE(entry.UpdateTime(), creation_time_us);
   std::unique_ptr<reading_list::ReadingListLocal> distilled_pb_entry(
       entry.AsReadingListLocal(base::Time::FromTimeT(40)));
@@ -348,6 +354,7 @@
   EXPECT_EQ(distilled_pb_entry->distillation_time_us(),
             entry.DistillationTime());
   EXPECT_EQ(distilled_pb_entry->distillation_size(), entry.DistillationSize());
+  EXPECT_TRUE(distilled_pb_entry->content_suggestions_extra().dismissed());
 }
 
 // Tests that the reading list entry is correctly parsed from
@@ -373,6 +380,10 @@
   pb_entry->set_distillation_time_us(now);
   pb_entry->set_distillation_size(50);
 
+  reading_list::ReadingListContentSuggestionsExtra* pb_extra =
+      pb_entry->mutable_content_suggestions_extra();
+  pb_extra->set_dismissed(true);
+
   std::unique_ptr<ReadingListEntry> waiting_entry(
       ReadingListEntry::FromReadingListLocal(*pb_entry,
                                              base::Time::FromTimeT(20)));
@@ -385,6 +396,7 @@
   EXPECT_EQ(waiting_entry->DistilledPath(), base::FilePath());
   EXPECT_EQ(waiting_entry->DistillationSize(), 50);
   EXPECT_EQ(waiting_entry->DistillationTime(), now);
+  EXPECT_TRUE(waiting_entry->ContentSuggestionsExtra()->dismissed);
   // Allow twice the jitter as test is not instantaneous.
   double fuzzing = 2 * ReadingListEntry::kBackoffPolicy.jitter_factor;
   int nextTry = waiting_entry->TimeUntilNextTry().InMinutes();
diff --git a/components/reading_list/core/reading_list_model.h b/components/reading_list/core/reading_list_model.h
index 7666cce..d003158 100644
--- a/components/reading_list/core/reading_list_model.h
+++ b/components/reading_list/core/reading_list_model.h
@@ -130,6 +130,10 @@
                                      int64_t distilation_size,
                                      const base::Time& distilation_time) = 0;
 
+  // Sets the extra info for the entry |url|.
+  virtual void SetContentSuggestionsExtra(
+      const GURL& url,
+      const reading_list::ContentSuggestionsExtra& extra) = 0;
   // Observer registration methods. The model will remove all observers upon
   // destruction automatically.
   void AddObserver(ReadingListModelObserver* observer);
diff --git a/components/reading_list/core/reading_list_model_impl.cc b/components/reading_list/core/reading_list_model_impl.cc
index 6b60704..d1ed193 100644
--- a/components/reading_list/core/reading_list_model_impl.cc
+++ b/components/reading_list/core/reading_list_model_impl.cc
@@ -461,6 +461,28 @@
   }
 }
 
+void ReadingListModelImpl::SetContentSuggestionsExtra(
+    const GURL& url,
+    const reading_list::ContentSuggestionsExtra& extra) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(loaded());
+  ReadingListEntry* entry = GetMutableEntryFromURL(url);
+  if (!entry) {
+    return;
+  }
+
+  for (ReadingListModelObserver& observer : observers_) {
+    observer.ReadingListWillUpdateEntry(this, url);
+  }
+  entry->SetContentSuggestionsExtra(extra);
+  if (storage_layer_) {
+    storage_layer_->SaveEntry(*entry);
+  }
+  for (ReadingListModelObserver& observer : observers_) {
+    observer.ReadingListDidApplyChanges(this);
+  }
+}
+
 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
 ReadingListModelImpl::CreateBatchToken() {
   return base::MakeUnique<ReadingListModelImpl::ScopedReadingListBatchUpdate>(
diff --git a/components/reading_list/core/reading_list_model_impl.h b/components/reading_list/core/reading_list_model_impl.h
index d388d80..9e91e1e 100644
--- a/components/reading_list/core/reading_list_model_impl.h
+++ b/components/reading_list/core/reading_list_model_impl.h
@@ -80,6 +80,9 @@
                              const GURL& distilled_url,
                              int64_t distillation_size,
                              const base::Time& distillation_date) override;
+  void SetContentSuggestionsExtra(
+      const GURL& url,
+      const reading_list::ContentSuggestionsExtra& extra) override;
 
   void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) override;
   ReadingListEntry* SyncMergeEntry(
diff --git a/components/reading_list/core/reading_list_model_unittest.cc b/components/reading_list/core/reading_list_model_unittest.cc
index ee00dc77..a2b2b57 100644
--- a/components/reading_list/core/reading_list_model_unittest.cc
+++ b/components/reading_list/core/reading_list_model_unittest.cc
@@ -708,6 +708,21 @@
             entry->DistillationTime());
 }
 
+// Tests setting ContentSuggestionsExtra info on entry.
+TEST_F(ReadingListModelTest, UpdateContentSuggestionsExtra) {
+  const GURL gurl("http://example.com");
+  model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+  const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+  ClearCounts();
+
+  reading_list::ContentSuggestionsExtra extra;
+  extra.dismissed = true;
+
+  model_->SetContentSuggestionsExtra(gurl, extra);
+  AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+  EXPECT_EQ(extra.dismissed, entry->ContentSuggestionsExtra()->dismissed);
+}
+
 // Tests that ReadingListModel calls CallbackModelBeingDeleted when destroyed.
 TEST_F(ReadingListModelTest, CallbackModelBeingDeleted) {
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
diff --git a/components/search_engines/template_url_data_util.cc b/components/search_engines/template_url_data_util.cc
index ab76c92..fd3138b 100644
--- a/components/search_engines/template_url_data_util.cc
+++ b/components/search_engines/template_url_data_util.cc
@@ -90,7 +90,7 @@
   if (dict.GetList(DefaultSearchManager::kAlternateURLs, &alternate_urls)) {
     for (const auto& it : *alternate_urls) {
       std::string alternate_url;
-      if (it->GetAsString(&alternate_url))
+      if (it.GetAsString(&alternate_url))
         result->alternate_urls.push_back(std::move(alternate_url));
     }
   }
@@ -99,7 +99,7 @@
   if (dict.GetList(DefaultSearchManager::kInputEncodings, &encodings)) {
     for (const auto& it : *encodings) {
       std::string encoding;
-      if (it->GetAsString(&encoding))
+      if (it.GetAsString(&encoding))
         result->input_encodings.push_back(std::move(encoding));
     }
   }
diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc
index dd3f339..129b124 100644
--- a/components/signin/core/browser/account_tracker_service.cc
+++ b/components/signin/core/browser/account_tracker_service.cc
@@ -350,7 +350,7 @@
           contains_deprecated_service_flags = true;
           std::string flag_string;
           for (const auto& flag : *service_flags_list) {
-            if (flag->GetAsString(&flag_string) &&
+            if (flag.GetAsString(&flag_string) &&
                 flag_string == kChildAccountServiceFlag) {
               is_child_account = true;
               break;
@@ -407,6 +407,8 @@
   if (!dict) {
     dict = new base::DictionaryValue();
     update->Append(base::WrapUnique(dict));
+    // |dict| is invalidated at this point, so it needs to be reset.
+    update->GetDictionary(update->GetSize() - 1, &dict);
     dict->SetString(kAccountKeyPath, account_id_16);
   }
 
diff --git a/components/ssl_config/ssl_config_service_manager_pref.cc b/components/ssl_config/ssl_config_service_manager_pref.cc
index 4ab20fb..df78e58 100644
--- a/components/ssl_config/ssl_config_service_manager_pref.cc
+++ b/components/ssl_config/ssl_config_service_manager_pref.cc
@@ -41,7 +41,7 @@
   std::string s;
   for (base::ListValue::const_iterator it = value->begin(); it != value->end();
        ++it) {
-    if (!(*it)->GetAsString(&s))
+    if (!it->GetAsString(&s))
       continue;
     results.push_back(s);
   }
diff --git a/components/subresource_filter/core/common/indexed_ruleset.cc b/components/subresource_filter/core/common/indexed_ruleset.cc
index 73cfd59f..341968a 100644
--- a/components/subresource_filter/core/common/indexed_ruleset.cc
+++ b/components/subresource_filter/core/common/indexed_ruleset.cc
@@ -124,10 +124,6 @@
     static_assert(
         proto::ELEMENT_TYPE_ALL <= std::numeric_limits<uint16_t>::max(),
         "Element types can not be stored in uint16_t.");
-    if ((rule_.element_types() & proto::ELEMENT_TYPE_ALL) !=
-        rule_.element_types()) {
-      return false;  // Unsupported element types.
-    }
     element_types_ = static_cast<uint16_t>(rule_.element_types());
 
     // Note: Normally we can not distinguish between the main plugin resource
@@ -135,6 +131,8 @@
     if (element_types_ & proto::ELEMENT_TYPE_OBJECT_SUBREQUEST)
       element_types_ |= proto::ELEMENT_TYPE_OBJECT;
 
+    // Ignore unknown element types.
+    element_types_ &= proto::ELEMENT_TYPE_ALL;
     // Filtering popups is not supported.
     element_types_ &= ~proto::ELEMENT_TYPE_POPUP;
 
@@ -145,12 +143,10 @@
     static_assert(
         proto::ACTIVATION_TYPE_ALL <= std::numeric_limits<uint8_t>::max(),
         "Activation types can not be stored in uint8_t.");
-    if ((rule_.activation_types() & proto::ACTIVATION_TYPE_ALL) !=
-        rule_.activation_types()) {
-      return false;  // Unsupported activation types.
-    }
     activation_types_ = static_cast<uint8_t>(rule_.activation_types());
 
+    // Ignore unknown activation types.
+    activation_types_ &= proto::ACTIVATION_TYPE_ALL;
     // No need in CSS activation, because the CSS rules are not supported.
     activation_types_ &=
         ~(proto::ACTIVATION_TYPE_ELEMHIDE | proto::ACTIVATION_TYPE_GENERICHIDE);
diff --git a/components/subresource_filter/core/common/indexed_ruleset_unittest.cc b/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
index 0fed408d..c89dc21 100644
--- a/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
+++ b/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
@@ -28,6 +28,14 @@
 constexpr proto::SourceType kFirstParty = proto::SOURCE_TYPE_FIRST_PARTY;
 constexpr proto::SourceType kThirdParty = proto::SOURCE_TYPE_THIRD_PARTY;
 
+constexpr proto::ElementType kAllElementTypes = proto::ELEMENT_TYPE_ALL;
+constexpr proto::ElementType kOther = proto::ELEMENT_TYPE_OTHER;
+constexpr proto::ElementType kImage = proto::ELEMENT_TYPE_IMAGE;
+constexpr proto::ElementType kFont = proto::ELEMENT_TYPE_FONT;
+constexpr proto::ElementType kScript = proto::ELEMENT_TYPE_SCRIPT;
+constexpr proto::ElementType kPopup = proto::ELEMENT_TYPE_POPUP;
+constexpr proto::ElementType kWebSocket = proto::ELEMENT_TYPE_WEBSOCKET;
+
 constexpr proto::ActivationType kDocument = proto::ACTIVATION_TYPE_DOCUMENT;
 constexpr proto::ActivationType kGenericBlock =
     proto::ACTIVATION_TYPE_GENERICBLOCK;
@@ -50,7 +58,7 @@
                                      : proto::RULE_SEMANTICS_BLACKLIST);
 
     rule_.set_source_type(source_type);
-    rule_.set_element_types(proto::ELEMENT_TYPE_ALL);
+    rule_.set_element_types(kAllElementTypes);
 
     rule_.set_url_pattern_type(url_pattern.type());
     rule_.set_anchor_left(url_pattern.anchor_left());
@@ -94,7 +102,7 @@
  protected:
   bool ShouldAllow(const char* url,
                    const char* document_origin = nullptr,
-                   proto::ElementType element_type = proto::ELEMENT_TYPE_OTHER,
+                   proto::ElementType element_type = kOther,
                    bool disable_generic_rules = false) const {
     DCHECK_NE(matcher_.get(), nullptr);
     url::Origin origin = GetOrigin(document_origin);
@@ -106,8 +114,7 @@
   bool ShouldAllow(const char* url,
                    const char* document_origin,
                    bool disable_generic_rules) const {
-    return ShouldAllow(url, document_origin, proto::ELEMENT_TYPE_OTHER,
-                       disable_generic_rules);
+    return ShouldAllow(url, document_origin, kOther, disable_generic_rules);
   }
 
   bool ShouldDeactivate(const char* document_url,
@@ -464,13 +471,7 @@
 }
 
 TEST_F(SubresourceFilterIndexedRulesetTest, OneRuleWithElementTypes) {
-  constexpr proto::ElementType kAll = proto::ELEMENT_TYPE_ALL;
-  constexpr proto::ElementType kImage = proto::ELEMENT_TYPE_IMAGE;
-  constexpr proto::ElementType kFont = proto::ELEMENT_TYPE_FONT;
-  constexpr proto::ElementType kScript = proto::ELEMENT_TYPE_SCRIPT;
-  constexpr proto::ElementType kPopup = proto::ELEMENT_TYPE_POPUP;
-  constexpr proto::ElementType kWebSocket = proto::ELEMENT_TYPE_WEBSOCKET;
-
+  constexpr auto kAll = kAllElementTypes;
   const struct {
     const char* url_pattern;
     int32_t element_types;
@@ -706,7 +707,7 @@
   EXPECT_TRUE(ShouldAllow("https://xample.com"));
 }
 
-TEST_F(SubresourceFilterIndexedRulesetTest, RuleWithUnsupportedOptions) {
+TEST_F(SubresourceFilterIndexedRulesetTest, RuleWithUnsupportedTypes) {
   const struct {
     int element_types;
     int activation_types;
@@ -715,7 +716,7 @@
       {0, proto::ACTIVATION_TYPE_MAX << 1},
       {proto::ELEMENT_TYPE_MAX << 1, proto::ACTIVATION_TYPE_MAX << 1},
 
-      {proto::ELEMENT_TYPE_POPUP, 0},
+      {kPopup, 0},
       {0, proto::ACTIVATION_TYPE_ELEMHIDE},
       {0, proto::ACTIVATION_TYPE_GENERICHIDE},
       {0, proto::ACTIVATION_TYPE_ELEMHIDE | proto::ACTIVATION_TYPE_GENERICHIDE},
@@ -735,4 +736,33 @@
   EXPECT_FALSE(ShouldAllow("https://exmpl.com/"));
 }
 
+TEST_F(SubresourceFilterIndexedRulesetTest,
+       RuleWithSupportedAndUnsupportedTypes) {
+  const struct {
+    int element_types;
+    int activation_types;
+  } kRules[] = {
+      {kImage | (proto::ELEMENT_TYPE_MAX << 1), 0},
+      {kScript | kPopup, 0},
+      {0, kDocument | (proto::ACTIVATION_TYPE_MAX << 1)},
+  };
+
+  for (const auto& rule : kRules) {
+    UrlRuleBuilder builder(UrlPattern("example.com"));
+    builder.rule().set_element_types(rule.element_types);
+    builder.rule().set_activation_types(rule.activation_types);
+    if (rule.activation_types)
+      builder.rule().set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+    EXPECT_TRUE(indexer_.AddUrlRule(builder.rule()));
+  }
+  Finish();
+
+  EXPECT_FALSE(ShouldAllow("http://example.com/", nullptr, kImage));
+  EXPECT_FALSE(ShouldAllow("http://example.com/", nullptr, kScript));
+  EXPECT_TRUE(ShouldAllow("http://example.com/"));
+
+  EXPECT_TRUE(ShouldDeactivate("http://example.com", nullptr, kDocument));
+  EXPECT_FALSE(ShouldDeactivate("http://example.com", nullptr, kGenericBlock));
+}
+
 }  // namespace subresource_filter
diff --git a/components/sync/base/cryptographer.cc b/components/sync/base/cryptographer.cc
index c585965..67e24ad9 100644
--- a/components/sync/base/cryptographer.cc
+++ b/components/sync/base/cryptographer.cc
@@ -34,10 +34,10 @@
       default_nigori_name_(other.default_nigori_name_) {
   for (NigoriMap::const_iterator it = other.nigoris_.begin();
        it != other.nigoris_.end(); ++it) {
-    std::string user_key, encryption_key, mac_key;
-    it->second->ExportKeys(&user_key, &encryption_key, &mac_key);
+    std::string encryption_key, mac_key;
+    it->second->ExportKeys(&encryption_key, &mac_key);
     linked_ptr<Nigori> nigori_copy(new Nigori());
-    nigori_copy->InitByImport(user_key, encryption_key, mac_key);
+    nigori_copy->InitByImport(encryption_key, mac_key);
     nigoris_.insert(std::make_pair(it->first, nigori_copy));
   }
 
@@ -150,8 +150,7 @@
     const Nigori& nigori = *it->second;
     sync_pb::NigoriKey* key = bag.add_key();
     key->set_name(it->first);
-    nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
-                      key->mutable_mac_key());
+    nigori.ExportKeys(key->mutable_encryption_key(), key->mutable_mac_key());
   }
 
   // Encrypt the bag with the default Nigori.
@@ -306,8 +305,7 @@
     // Only use this key if we don't already know about it.
     if (nigoris_.end() == nigoris_.find(key.name())) {
       std::unique_ptr<Nigori> new_nigori(new Nigori);
-      if (!new_nigori->InitByImport(key.user_key(), key.encryption_key(),
-                                    key.mac_key())) {
+      if (!new_nigori->InitByImport(key.encryption_key(), key.mac_key())) {
         NOTREACHED();
         continue;
       }
@@ -348,8 +346,7 @@
   if (iter == nigoris_.end())
     return std::string();
   sync_pb::NigoriKey key;
-  if (!iter->second->ExportKeys(key.mutable_user_key(),
-                                key.mutable_encryption_key(),
+  if (!iter->second->ExportKeys(key.mutable_encryption_key(),
                                 key.mutable_mac_key()))
     return std::string();
   return key.SerializeAsString();
@@ -364,8 +361,7 @@
     return false;
 
   std::unique_ptr<Nigori> nigori(new Nigori);
-  if (!nigori->InitByImport(key.user_key(), key.encryption_key(),
-                            key.mac_key())) {
+  if (!nigori->InitByImport(key.encryption_key(), key.mac_key())) {
     NOTREACHED();
     return false;
   }
diff --git a/components/sync/base/nigori.cc b/components/sync/base/nigori.cc
index e87e96c..03c61b2 100644
--- a/components/sync/base/nigori.cc
+++ b/components/sync/base/nigori.cc
@@ -79,12 +79,6 @@
   if (!user_salt->GetRawKey(&raw_user_salt))
     return false;
 
-  // Kuser = PBKDF2(P, Suser, Nuser, 16)
-  user_key_ = SymmetricKey::DeriveKeyFromPassword(
-      SymmetricKey::AES, password, raw_user_salt, kUserIterations,
-      kDerivedKeySizeInBits);
-  DCHECK(user_key_);
-
   // Kenc = PBKDF2(P, Suser, Nenc, 16)
   encryption_key_ = SymmetricKey::DeriveKeyFromPassword(
       SymmetricKey::AES, password, raw_user_salt, kEncryptionIterations,
@@ -97,14 +91,11 @@
       kDerivedKeySizeInBits);
   DCHECK(mac_key_);
 
-  return user_key_ && encryption_key_ && mac_key_;
+  return encryption_key_ && mac_key_;
 }
 
-bool Nigori::InitByImport(const std::string& user_key,
-                          const std::string& encryption_key,
+bool Nigori::InitByImport(const std::string& encryption_key,
                           const std::string& mac_key) {
-  user_key_ = SymmetricKey::Import(SymmetricKey::AES, user_key);
-
   encryption_key_ = SymmetricKey::Import(SymmetricKey::AES, encryption_key);
   DCHECK(encryption_key_);
 
@@ -232,14 +223,11 @@
   return true;
 }
 
-bool Nigori::ExportKeys(std::string* user_key,
-                        std::string* encryption_key,
+bool Nigori::ExportKeys(std::string* encryption_key,
                         std::string* mac_key) const {
   DCHECK(encryption_key);
   DCHECK(mac_key);
 
-  user_key_->GetRawKey(user_key);
-
   return encryption_key_->GetRawKey(encryption_key) &&
          mac_key_->GetRawKey(mac_key);
 }
diff --git a/components/sync/base/nigori.h b/components/sync/base/nigori.h
index 92c77b40..d0088c3 100644
--- a/components/sync/base/nigori.h
+++ b/components/sync/base/nigori.h
@@ -41,8 +41,7 @@
 
   // Initialize the client by importing the given keys instead of deriving new
   // ones.
-  bool InitByImport(const std::string& user_key,
-                    const std::string& encryption_key,
+  bool InitByImport(const std::string& encryption_key,
                     const std::string& mac_key);
 
   // Derives a secure lookup name from |type| and |name|. If |hostname|,
@@ -60,9 +59,7 @@
   bool Decrypt(const std::string& value, std::string* decrypted) const;
 
   // Exports the raw derived keys.
-  bool ExportKeys(std::string* user_key,
-                  std::string* encryption_key,
-                  std::string* mac_key) const;
+  bool ExportKeys(std::string* encryption_key, std::string* mac_key) const;
 
   static const char kSaltSalt[];  // The salt used to derive the user salt.
   static const size_t kSaltKeySizeInBits = 128;
@@ -71,16 +68,10 @@
   static const size_t kHashSize = 32;
 
   static const size_t kSaltIterations = 1001;
-  static const size_t kUserIterations = 1002;
   static const size_t kEncryptionIterations = 1003;
   static const size_t kSigningIterations = 1004;
 
  private:
-  // user_key isn't used any more, but legacy clients will fail to import a
-  // nigori node without one. We preserve it for the sake of those clients, but
-  // it should be removed once enough clients have upgraded to code that doesn't
-  // enforce its presence.
-  std::unique_ptr<crypto::SymmetricKey> user_key_;
   std::unique_ptr<crypto::SymmetricKey> encryption_key_;
   std::unique_ptr<crypto::SymmetricKey> mac_key_;
 };
diff --git a/components/sync/base/nigori_unittest.cc b/components/sync/base/nigori_unittest.cc
index 4dd0e22..09bbcd75 100644
--- a/components/sync/base/nigori_unittest.cc
+++ b/components/sync/base/nigori_unittest.cc
@@ -126,13 +126,12 @@
   Nigori nigori1;
   EXPECT_TRUE(nigori1.InitByDerivation("example.com", "username", "password"));
 
-  std::string user_key;
   std::string encryption_key;
   std::string mac_key;
-  EXPECT_TRUE(nigori1.ExportKeys(&user_key, &encryption_key, &mac_key));
+  EXPECT_TRUE(nigori1.ExportKeys(&encryption_key, &mac_key));
 
   Nigori nigori2;
-  EXPECT_TRUE(nigori2.InitByImport(user_key, encryption_key, mac_key));
+  EXPECT_TRUE(nigori2.InitByImport(encryption_key, mac_key));
 
   std::string original("test");
   std::string plaintext;
@@ -152,30 +151,5 @@
   EXPECT_EQ(permuted1, permuted2);
 }
 
-TEST(SyncNigoriTest, InitByDerivationSetsUserKey) {
-  Nigori nigori;
-  EXPECT_TRUE(nigori.InitByDerivation("example.com", "username", "password"));
-
-  std::string user_key = "";
-  std::string encryption_key;
-  std::string mac_key;
-  EXPECT_TRUE(nigori.ExportKeys(&user_key, &encryption_key, &mac_key));
-
-  EXPECT_NE(user_key, "");
-}
-
-TEST(SyncNigoriTest, InitByImportToleratesEmptyUserKey) {
-  Nigori nigori1;
-  EXPECT_TRUE(nigori1.InitByDerivation("example.com", "username", "password"));
-
-  std::string user_key;
-  std::string encryption_key;
-  std::string mac_key;
-  EXPECT_TRUE(nigori1.ExportKeys(&user_key, &encryption_key, &mac_key));
-
-  Nigori nigori2;
-  EXPECT_TRUE(nigori2.InitByImport("", encryption_key, mac_key));
-}
-
 }  // anonymous namespace
 }  // namespace syncer
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index c6c6a8b..b1863dcb 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/location.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
@@ -76,6 +77,12 @@
   section->SetString("title", title);
   section->Set("data", section_contents);
   section->SetBoolean("is_sensitive", false);
+  // If the following |Append| results in a reallocation, pointers to the
+  // members of |parent_list| will be invalidated. This would result in
+  // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is
+  // necessary to ensure no reallocation takes place.
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  CHECK_LT(parent_list->GetSize(), parent_list->capacity());
   parent_list->Append(std::move(section));
   return section_contents;
 }
@@ -89,6 +96,12 @@
   section->SetString("title", title);
   section->Set("data", section_contents);
   section->SetBoolean("is_sensitive", true);
+  // If the following |Append| results in a reallocation, pointers to
+  // |parent_list| and its members will be invalidated. This would result in
+  // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is
+  // necessary to ensure no reallocation takes place.
+  CHECK_LT(parent_list->GetSize(), parent_list->capacity());
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
   parent_list->Append(std::move(section));
   return section_contents;
 }
@@ -117,7 +130,14 @@
   stat_->SetString("stat_name", key);
   stat_->SetString("stat_value", "Uninitialized");
   stat_->SetBoolean("is_valid", false);
+  // |stat_| will be invalidated by |Append|, so it needs to be reset.
+  // Furthermore, if |Append| results in a reallocation, |stat_| members of
+  // other SyncStats will be invalidated. This is why the following check is
+  // necessary, so that it is guaranteed that a reallocation will not happen.
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  CHECK_LT(section->GetSize(), section->capacity());
   section->Append(base::WrapUnique(stat_));
+  section->GetDictionary(section->GetSize() - 1, &stat_);
 }
 
 void StringSyncStat::SetValue(const std::string& value) {
@@ -145,7 +165,14 @@
   stat_->SetString("stat_name", key);
   stat_->SetBoolean("stat_value", false);
   stat_->SetBoolean("is_valid", false);
+  // |stat_| will be invalidated by |Append|, so it needs to be reset.
+  // Furthermore, if |Append| results in a reallocation, |stat_| members of
+  // other SyncStats will be invalidated. This is why the following check is
+  // necessary, so that it is guaranteed that a reallocation will not happen.
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  CHECK_LT(section->GetSize(), section->capacity());
   section->Append(base::WrapUnique(stat_));
+  section->GetDictionary(section->GetSize() - 1, &stat_);
 }
 
 void BoolSyncStat::SetValue(bool value) {
@@ -168,7 +195,14 @@
   stat_->SetString("stat_name", key);
   stat_->SetInteger("stat_value", 0);
   stat_->SetBoolean("is_valid", false);
+  // |stat_| will be invalidated by |Append|, so it needs to be reset.
+  // Furthermore, if |Append| results in a reallocation, |stat_| members of
+  // other SyncStats will be invalidated. This is why the following check is
+  // necessary, so that it is guaranteed that a reallocation will not happen.
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  CHECK_LT(section->GetSize(), section->capacity());
   section->Append(base::WrapUnique(stat_));
+  section->GetDictionary(section->GetSize() - 1, &stat_);
 }
 
 void IntSyncStat::SetValue(int value) {
@@ -251,24 +285,34 @@
 
   // 'details': A list of sections.
   base::ListValue* stats_list = new base::ListValue();
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  stats_list->Reserve(12);
 
   // The following lines define the sections and their fields.  For each field,
   // a class is instantiated, which allows us to reference the fields in
   // 'setter' code later on in this function.
   base::ListValue* section_summary = AddSection(stats_list, "Summary");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_summary->Reserve(1);
   StringSyncStat summary_string(section_summary, "Summary");
 
   base::ListValue* section_version = AddSection(stats_list, "Version Info");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_version->Reserve(2);
   StringSyncStat client_version(section_version, "Client Version");
   StringSyncStat server_url(section_version, "Server URL");
 
   base::ListValue* section_identity =
       AddSensitiveSection(stats_list, kIdentityTitle);
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_identity->Reserve(3);
   StringSyncStat sync_id(section_identity, "Sync Client ID");
   StringSyncStat invalidator_id(section_identity, "Invalidator Client ID");
   StringSyncStat username(section_identity, "Username");
 
   base::ListValue* section_credentials = AddSection(stats_list, "Credentials");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_credentials->Reserve(4);
   StringSyncStat request_token_time(section_credentials, "Requested Token");
   StringSyncStat receive_token_time(section_credentials, "Received Token");
   StringSyncStat token_request_status(section_credentials,
@@ -276,6 +320,8 @@
   StringSyncStat next_token_request(section_credentials, "Next Token Request");
 
   base::ListValue* section_local = AddSection(stats_list, "Local State");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_local->Reserve(7);
   StringSyncStat server_connection(section_local, "Server Connection");
   StringSyncStat last_synced(section_local, "Last Synced");
   BoolSyncStat is_setup_complete(section_local,
@@ -288,12 +334,16 @@
   StringSyncStat local_backend_path(section_local, "Local backend path");
 
   base::ListValue* section_network = AddSection(stats_list, "Network");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_network->Reserve(3);
   BoolSyncStat is_throttled(section_network, "Throttled");
   StringSyncStat retry_time(section_network, "Retry time (maybe stale)");
   BoolSyncStat are_notifications_enabled(section_network,
                                          "Notifications Enabled");
 
   base::ListValue* section_encryption = AddSection(stats_list, "Encryption");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_encryption->Reserve(9);
   BoolSyncStat is_using_explicit_passphrase(section_encryption,
                                             "Explicit Passphrase");
   BoolSyncStat is_passphrase_required(section_encryption,
@@ -311,12 +361,16 @@
 
   base::ListValue* section_last_session =
       AddSection(stats_list, "Status from Last Completed Session");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_last_session->Reserve(4);
   StringSyncStat session_source(section_last_session, "Sync Source");
   StringSyncStat get_key_result(section_last_session, "GetKey Step Result");
   StringSyncStat download_result(section_last_session, "Download Step Result");
   StringSyncStat commit_result(section_last_session, "Commit Step Result");
 
   base::ListValue* section_counters = AddSection(stats_list, "Running Totals");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_counters->Reserve(7);
   IntSyncStat notifications_received(section_counters,
                                      "Notifications Received");
   IntSyncStat updates_received(section_counters, "Updates Downloaded");
@@ -330,6 +384,8 @@
 
   base::ListValue* section_this_cycle =
       AddSection(stats_list, "Transient Counters (this cycle)");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_this_cycle->Reserve(4);
   IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts");
   IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts");
   IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts");
@@ -337,12 +393,16 @@
 
   base::ListValue* section_that_cycle = AddSection(
       stats_list, "Transient Counters (last cycle of last completed session)");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_that_cycle->Reserve(3);
   IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded");
   IntSyncStat committed_count(section_that_cycle, "Committed Count");
   IntSyncStat entries(section_that_cycle, "Entries");
 
   base::ListValue* section_nudge_info =
       AddSection(stats_list, "Nudge Source Counters");
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  section_nudge_info->Reserve(3);
   IntSyncStat nudge_source_notification(section_nudge_info,
                                         "Server Invalidations");
   IntSyncStat nudge_source_local(section_nudge_info, "Local Changes");
@@ -491,6 +551,8 @@
   // actionable_error_detected is set.
 
   base::ListValue* actionable_error = new base::ListValue();
+  // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
+  actionable_error->Reserve(4);
   about_info->Set("actionable_error", actionable_error);
 
   StringSyncStat error_type(actionable_error, "Error Type");
diff --git a/components/sync/driver/async_directory_type_controller_unittest.cc b/components/sync/driver/async_directory_type_controller_unittest.cc
index 7d21100..c28c2bb 100644
--- a/components/sync/driver/async_directory_type_controller_unittest.cc
+++ b/components/sync/driver/async_directory_type_controller_unittest.cc
@@ -56,10 +56,6 @@
   event->Signal();
 }
 
-ACTION_P(SaveChangeProcessor, scoped_change_processor) {
-  scoped_change_processor->reset(arg2);
-}
-
 class SharedChangeProcessorMock : public SharedChangeProcessor {
  public:
   explicit SharedChangeProcessorMock(ModelType type)
diff --git a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
index dc376d9f..2ef2da8 100644
--- a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
@@ -59,10 +59,6 @@
 static const base::FilePath::CharType kTestSyncDir[] =
     FILE_PATH_LITERAL("sync-test");
 
-ACTION_P(Signal, event) {
-  event->Signal();
-}
-
 void EmptyNetworkTimeUpdate(const base::Time&,
                             const base::TimeDelta&,
                             const base::TimeDelta&) {}
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index cc4fd1bd..486afe1 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -216,8 +216,7 @@
       sync_pb::NigoriKey* key = bag.add_key();
 
       key->set_name(GetNigoriName(nigori));
-      nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
-                        key->mutable_mac_key());
+      nigori.ExportKeys(key->mutable_encryption_key(), key->mutable_mac_key());
     }
 
     // Re-create the last nigori from that loop.
diff --git a/components/sync/syncable/model_type.cc b/components/sync/syncable/model_type.cc
index bf266b3..942b898 100644
--- a/components/sync/syncable/model_type.cc
+++ b/components/sync/syncable/model_type.cc
@@ -653,7 +653,7 @@
   ModelTypeSet result;
   for (base::ListValue::const_iterator i = value.begin(); i != value.end();
        ++i) {
-    result.Put(ModelTypeFromValue(**i));
+    result.Put(ModelTypeFromValue(*i));
   }
   return result;
 }
diff --git a/components/sync/test/fake_server/fake_server_verifier.cc b/components/sync/test/fake_server/fake_server_verifier.cc
index 10f68c81..f74c28f7 100644
--- a/components/sync/test/fake_server/fake_server_verifier.cc
+++ b/components/sync/test/fake_server/fake_server_verifier.cc
@@ -106,7 +106,7 @@
   if (entities->GetList(model_type_string, &entity_list)) {
     base::Value name_value(name);
     for (const auto& entity : *entity_list) {
-      if (name_value.Equals(entity.get()))
+      if (name_value.Equals(&entity))
         actual_count++;
     }
   }
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index bf7a081..6bb1801 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -291,7 +291,7 @@
   base::ListValue* result = to_list_value.DeepCopy();
 
   for (const auto& value : from_list_value) {
-    result->AppendIfNotPresent(value->CreateDeepCopy());
+    result->AppendIfNotPresent(value.CreateDeepCopy());
   }
   return result;
 }
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 6e8f7b6..63d758b 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -616,8 +616,7 @@
     const base::DictionaryValue* item = nullptr;
     std::string language;
     double probability = 0.0;
-    if (entry->GetAsDictionary(&item) &&
-        item->GetString(kLanguage, &language) &&
+    if (entry.GetAsDictionary(&item) && item->GetString(kLanguage, &language) &&
         item->GetDouble(kProbability, &probability)) {
       // Normalize the the language code known and supported by
       // Translate.
diff --git a/components/translate/ios/browser/BUILD.gn b/components/translate/ios/browser/BUILD.gn
index b514e91..f4b7c5d 100644
--- a/components/translate/ios/browser/BUILD.gn
+++ b/components/translate/ios/browser/BUILD.gn
@@ -43,6 +43,7 @@
 }
 
 source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "js_translate_manager_unittest.mm",
diff --git a/components/translate/ios/browser/js_translate_manager_unittest.mm b/components/translate/ios/browser/js_translate_manager_unittest.mm
index 696ecee..76bf4f84 100644
--- a/components/translate/ios/browser/js_translate_manager_unittest.mm
+++ b/components/translate/ios/browser/js_translate_manager_unittest.mm
@@ -4,7 +4,6 @@
 
 #import "components/translate/ios/browser/js_translate_manager.h"
 
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/grit/components_resources.h"
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
@@ -13,6 +12,10 @@
 #include "testing/platform_test.h"
 #include "ui/base/resource/resource_bundle.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface JsTranslateManager (Testing)
 - (double)performanceNow;
 @end
@@ -28,8 +31,8 @@
 class JsTranslateManagerTest : public PlatformTest {
  protected:
   JsTranslateManagerTest() {
-    receiver_.reset([[CRWTestJSInjectionReceiver alloc] init]);
-    manager_.reset([[JsTranslateManager alloc] initWithReceiver:receiver_]);
+    receiver_ = [[CRWTestJSInjectionReceiver alloc] init];
+    manager_ = [[JsTranslateManager alloc] initWithReceiver:receiver_];
     base::StringPiece script =
         ResourceBundle::GetSharedInstance().GetRawDataResource(
             IDR_TRANSLATE_JS);
@@ -43,8 +46,8 @@
     return [web::ExecuteJavaScript(receiver_, script) boolValue];
   }
 
-  base::scoped_nsobject<CRWTestJSInjectionReceiver> receiver_;
-  base::scoped_nsobject<JsTranslateManager> manager_;
+  CRWTestJSInjectionReceiver* receiver_;
+  JsTranslateManager* manager_;
 };
 
 // TODO(crbug.com/658619#c47): Test reported as flaky.
diff --git a/components/translate/ios/browser/language_detection_controller_unittest.mm b/components/translate/ios/browser/language_detection_controller_unittest.mm
index d40361b..c22a8d9 100644
--- a/components/translate/ios/browser/language_detection_controller_unittest.mm
+++ b/components/translate/ios/browser/language_detection_controller_unittest.mm
@@ -5,6 +5,7 @@
 #import "components/translate/ios/browser/language_detection_controller.h"
 
 #include "base/mac/bind_objc_block.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
@@ -14,6 +15,10 @@
 #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
+
 @interface MockJsLanguageDetectionManager : JsLanguageDetectionManager
 @end
 
@@ -33,10 +38,10 @@
   LanguageDetectionControllerTest() {
     prefs_.registry()->RegisterBooleanPref(prefs::kEnableTranslate, true);
 
-    base::scoped_nsobject<MockJsLanguageDetectionManager> js_manager(
-        [[MockJsLanguageDetectionManager alloc] init]);
-    controller_.reset(new LanguageDetectionController(
-        &web_state_, js_manager.get(), &prefs_));
+    MockJsLanguageDetectionManager* js_manager =
+        [[MockJsLanguageDetectionManager alloc] init];
+    controller_ = base::MakeUnique<LanguageDetectionController>(
+        &web_state_, js_manager, &prefs_);
   }
 
   LanguageDetectionController* controller() { return controller_.get(); }
@@ -57,7 +62,7 @@
 
   __block bool block_was_called = false;
   auto subscription =
-      controller()->RegisterLanguageDetectionCallback(base::BindBlock(
+      controller()->RegisterLanguageDetectionCallback(base::BindBlockArc(
           ^(const LanguageDetectionController::DetectionDetails& details) {
             block_was_called = true;
             EXPECT_EQ(kRootLanguage, details.html_root_language);
diff --git a/components/translate/ios/browser/translate_controller_unittest.mm b/components/translate/ios/browser/translate_controller_unittest.mm
index 44e55a3..99d5e36 100644
--- a/components/translate/ios/browser/translate_controller_unittest.mm
+++ b/components/translate/ios/browser/translate_controller_unittest.mm
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #import "components/translate/ios/browser/js_translate_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
@@ -13,6 +14,10 @@
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace translate {
 
 class TranslateControllerTest : public PlatformTest,
@@ -26,10 +31,10 @@
         translation_time_(0),
         on_script_ready_called_(false),
         on_translate_complete_called_(false) {
-    mock_js_translate_manager_.reset(
-        [[OCMockObject niceMockForClass:[JsTranslateManager class]] retain]);
-    translate_controller_.reset(new TranslateController(
-        test_web_state_.get(), mock_js_translate_manager_));
+    mock_js_translate_manager_ =
+        [OCMockObject niceMockForClass:[JsTranslateManager class]];
+    translate_controller_ = base::MakeUnique<TranslateController>(
+        test_web_state_.get(), mock_js_translate_manager_);
     translate_controller_->set_observer(this);
   }
 
@@ -53,7 +58,7 @@
   }
 
   std::unique_ptr<web::TestWebState> test_web_state_;
-  base::scoped_nsobject<id> mock_js_translate_manager_;
+  id mock_js_translate_manager_;
   std::unique_ptr<TranslateController> translate_controller_;
   bool success_;
   double ready_time_;
diff --git a/components/update_client/component_patcher.cc b/components/update_client/component_patcher.cc
index afafd874..0bd27dd 100644
--- a/components/update_client/component_patcher.cc
+++ b/components/update_client/component_patcher.cc
@@ -80,7 +80,7 @@
     return;
   }
   const base::DictionaryValue* command_args;
-  if (!(*next_command_)->GetAsDictionary(&command_args)) {
+  if (!next_command_->GetAsDictionary(&command_args)) {
     DonePatching(UnpackerError::kDeltaBadCommands, 0);
     return;
   }
diff --git a/components/url_matcher/url_matcher_factory.cc b/components/url_matcher/url_matcher_factory.cc
index 0a30497..253d022ec 100644
--- a/components/url_matcher/url_matcher_factory.cc
+++ b/components/url_matcher/url_matcher_factory.cc
@@ -250,10 +250,10 @@
 
   for (const auto& entry : *value_list) {
     int port = 0;
-    base::ListValue* range = NULL;
-    if (entry->GetAsInteger(&port)) {
+    const base::ListValue* range = NULL;
+    if (entry.GetAsInteger(&port)) {
       ranges.push_back(URLMatcherPortFilter::CreateRange(port));
-    } else if (entry->GetAsList(&range)) {
+    } else if (entry.GetAsList(&range)) {
       int from = 0, to = 0;
       if (range->GetSize() != 2u ||
           !range->GetInteger(0, &from) ||
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index bafff0c..9a3211f4 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1158,9 +1158,10 @@
     "renderer_host/media/audio_renderer_host.h",
     "renderer_host/media/audio_sync_reader.cc",
     "renderer_host/media/audio_sync_reader.h",
-    "renderer_host/media/buildable_video_capture_device.h",
     "renderer_host/media/in_process_buildable_video_capture_device.cc",
     "renderer_host/media/in_process_buildable_video_capture_device.h",
+    "renderer_host/media/in_process_video_capture_provider.cc",
+    "renderer_host/media/in_process_video_capture_provider.h",
     "renderer_host/media/media_capture_devices_impl.cc",
     "renderer_host/media/media_capture_devices_impl.h",
     "renderer_host/media/media_devices_dispatcher_host.cc",
@@ -1191,6 +1192,7 @@
     "renderer_host/media/video_capture_host.h",
     "renderer_host/media/video_capture_manager.cc",
     "renderer_host/media/video_capture_manager.h",
+    "renderer_host/media/video_capture_provider.h",
     "renderer_host/native_web_keyboard_event_aura.cc",
     "renderer_host/native_web_keyboard_event_mac.mm",
     "renderer_host/offscreen_canvas_compositor_frame_sink.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index ef954c9..67f70f1 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -89,7 +89,7 @@
   for (base::ListValue::const_iterator it = states_value->begin();
        it != states_value->end(); ++it) {
     std::string state_value;
-    if ((*it)->GetAsString(&state_value))
+    if (it->GetAsString(&state_value))
       WriteAttribute(true, state_value, &line);
   }
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index fe89c7b..b454ae2 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -404,7 +404,7 @@
              it != list_value->end();
              ++it) {
           base::string16 string_value;
-          if ((*it)->GetAsString(&string_value))
+          if (it->GetAsString(&string_value))
             WriteAttribute(false, string_value, &line);
         }
         break;
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.h b/content/browser/accessibility/browser_accessibility_manager_mac.h
index 5f5f83c..406793a 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.h
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.h
@@ -49,6 +49,10 @@
   void OnNodeDataWillChange(ui::AXTree* tree,
                             const ui::AXNodeData& old_node_data,
                             const ui::AXNodeData& new_node_data) override;
+  void OnStateChanged(ui::AXTree* tree,
+                      ui::AXNode* node,
+                      ui::AXState state,
+                      bool new_value) override;
 
   // Returns an autoreleased object.
   NSDictionary* GetUserInfoForSelectedTextChangedNotification();
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 37dd8fa..c6b659b 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -405,6 +405,18 @@
   }
 }
 
+void BrowserAccessibilityManagerMac::OnStateChanged(ui::AXTree* tree,
+                                                    ui::AXNode* ax_node,
+                                                    ui::AXState state,
+                                                    bool new_value) {
+  if (state != ui::AX_STATE_PRESSED)
+    return;
+
+  BrowserAccessibility* node = GetFromID(ax_node->id());
+  NotifyAccessibilityEvent(BrowserAccessibilityEvent::FromTreeChange,
+                           ui::AX_EVENT_CHECKED_STATE_CHANGED, node);
+}
+
 NSDictionary* BrowserAccessibilityManagerMac::
     GetUserInfoForSelectedTextChangedNotification() {
   NSMutableDictionary* user_info = [[[NSMutableDictionary alloc] init]
diff --git a/content/browser/android/java/gin_java_method_invocation_helper.cc b/content/browser/android/java/gin_java_method_invocation_helper.cc
index b4a3013..1a5d1df 100644
--- a/content/browser/android/java/gin_java_method_invocation_helper.cc
+++ b/content/browser/android/java/gin_java_method_invocation_helper.cc
@@ -57,12 +57,12 @@
   const base::ListValue* list;
   list_value.GetAsList(&list);
   for (const auto& entry : *list) {
-    if (AppendObjectRef(dispatcher, *entry))
+    if (AppendObjectRef(dispatcher, entry))
       continue;
-    if (entry->IsType(base::Value::Type::LIST)) {
-      BuildObjectRefsFromListValue(dispatcher, *entry);
-    } else if (entry->IsType(base::Value::Type::DICTIONARY)) {
-      BuildObjectRefsFromDictionaryValue(dispatcher, *entry);
+    if (entry.IsType(base::Value::Type::LIST)) {
+      BuildObjectRefsFromListValue(dispatcher, entry);
+    } else if (entry.IsType(base::Value::Type::DICTIONARY)) {
+      BuildObjectRefsFromDictionaryValue(dispatcher, entry);
     }
   }
 }
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index f35fc3f..98eb42d 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -293,7 +293,7 @@
   std::unique_ptr<AudioManagerThread> audio_thread_;
   media::ScopedAudioManagerPtr audio_manager_;
   // Calls to |audio_system_| must not be posted to the audio thread if it
-  // differs from the UI one.
+  // differs from the UI one. See http://crbug.com/705455.
   std::unique_ptr<media::AudioSystem> audio_system_;
 
   std::unique_ptr<midi::MidiService> midi_service_;
diff --git a/content/browser/browsing_data/clear_site_data_throttle.cc b/content/browser/browsing_data/clear_site_data_throttle.cc
index 00e57ce..eec778f 100644
--- a/content/browser/browsing_data/clear_site_data_throttle.cc
+++ b/content/browser/browsing_data/clear_site_data_throttle.cc
@@ -206,9 +206,9 @@
   *clear_cache = false;
 
   std::vector<std::string> type_names;
-  for (const std::unique_ptr<base::Value>& value : *types) {
+  for (const base::Value& value : *types) {
     std::string type;
-    value->GetAsString(&type);
+    value.GetAsString(&type);
 
     bool* datatype = nullptr;
 
@@ -221,7 +221,7 @@
     } else {
       std::string serialized_type;
       JSONStringValueSerializer serializer(&serialized_type);
-      serializer.Serialize(*value);
+      serializer.Serialize(value);
       ConsoleLog(
           messages, current_url_,
           base::StringPrintf("Invalid type: %s.", serialized_type.c_str()),
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index de70cc65..f522b3aa 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -63,7 +63,7 @@
   if (value.GetAsList(&list)) {
     std::unique_ptr<base::ListValue> out_list(new base::ListValue());
     for (const auto& value : *list)
-      out_list->Append(ConvertDictKeyStyle(*value));
+      out_list->Append(ConvertDictKeyStyle(value));
     return std::move(out_list);
   }
 
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index 9edd24a..ac9d3ee8 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -36,10 +36,6 @@
 
 namespace {
 
-MATCHER_P2(EqualsCaptureCapability, width, height, "") {
-  return arg.width == width && arg.height == height;
-}
-
 const int kTestFrameWidth1 = 500;
 const int kTestFrameHeight1 = 500;
 const int kTestFrameWidth2 = 400;
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc
index 4ad3575..8afe067 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager.cc
@@ -13,7 +13,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/audio/audio_input_ipc.h"
-#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_system.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
 #include "media/base/media_switches.h"
@@ -32,13 +32,12 @@
 }
 
 AudioInputDeviceManager::AudioInputDeviceManager(
-    media::AudioManager* audio_manager)
+    media::AudioSystem* audio_system)
     : next_capture_session_id_(kFirstSessionId),
 #if defined(OS_CHROMEOS)
       keyboard_mic_streams_count_(0),
 #endif
-      audio_manager_(audio_manager),
-      device_task_runner_(audio_manager_->GetTaskRunner()) {
+      audio_system_(audio_system) {
 }
 
 AudioInputDeviceManager::~AudioInputDeviceManager() {
@@ -58,7 +57,6 @@
     MediaStreamProviderListener* listener) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(listener);
-  DCHECK(device_task_runner_);
   listeners_.AddObserver(listener);
 }
 
@@ -73,10 +71,35 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Generate a new id for this device.
   int session_id = next_capture_session_id_++;
-  device_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread,
-                 this, session_id, device));
+
+  // base::Unretained(this) is safe, because AudioInputDeviceManager is
+  // destroyed not earlier than on the IO message loop destruction.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUseFakeDeviceForMediaStream)) {
+    audio_system_->GetAssociatedOutputDeviceID(
+        device.id,
+        base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
+                   base::Unretained(this), session_id, device,
+                   base::TimeTicks::Now(),
+                   media::AudioParameters::UnavailableDeviceParams(),
+                   media::AudioParameters::UnavailableDeviceParams()));
+  } else {
+    // TODO(tommi): As is, we hit this code path when device.type is
+    // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that
+    // the AudioManager can know about. This currently does not fail because
+    // the implementation of GetInputStreamParameters returns valid parameters
+    // by default for invalid devices. That behavior is problematic because it
+    // causes other parts of the code to attempt to open truly invalid or
+    // missing devices and falling back on alternate devices (and likely fail
+    // twice in a row). Tab audio capture should not pass through here and
+    // GetInputStreamParameters should return invalid parameters for invalid
+    // devices.
+
+    audio_system_->GetInputDeviceInfo(
+        device.id, base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
+                              base::Unretained(this), session_id, device,
+                              base::TimeTicks::Now()));
+  }
 
   return session_id;
 }
@@ -135,78 +158,34 @@
 }
 #endif
 
-void AudioInputDeviceManager::OpenOnDeviceThread(
+void AudioInputDeviceManager::OpenedOnIOThread(
     int session_id,
-    const MediaStreamDevice& device) {
-  SCOPED_UMA_HISTOGRAM_TIMER(
-      "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
-  DCHECK(IsOnDeviceThread());
-
-  StreamDeviceInfo out(device.type, device.name, device.id, 0, 0, 0);
-  out.session_id = session_id;
-
-  MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input;
-
-  // Add preferred output device information if a matching output device
-  // exists.
-  out.device.matched_output_device_id =
-      audio_manager_->GetAssociatedOutputDeviceID(device.id);
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeDeviceForMediaStream)) {
-    // Don't need to query the hardware information if using fake device.
-    input_params.sample_rate = 44100;
-    input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO;
-    if (!out.device.matched_output_device_id.empty()) {
-      out.device.matched_output.sample_rate = 44100;
-      out.device.matched_output.channel_layout = media::CHANNEL_LAYOUT_STEREO;
-    }
-  } else {
-    // TODO(tommi): As is, we hit this code path when device.type is
-    // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that
-    // the AudioManager can know about. This currently does not fail because
-    // the implementation of GetInputStreamParameters returns valid parameters
-    // by default for invalid devices. That behavior is problematic because it
-    // causes other parts of the code to attempt to open truly invalid or
-    // missing devices and falling back on alternate devices (and likely fail
-    // twice in a row). Tab audio capture should not pass through here and
-    // GetInputStreamParameters should return invalid parameters for invalid
-    // devices.
-
-    // Get the preferred sample rate and channel configuration for the
-    // audio device.
-    media::AudioParameters params =
-        audio_manager_->GetInputStreamParameters(device.id);
-    input_params.sample_rate = params.sample_rate();
-    input_params.channel_layout = params.channel_layout();
-    input_params.frames_per_buffer = params.frames_per_buffer();
-    input_params.effects = params.effects();
-    input_params.mic_positions = params.mic_positions();
-    if (!out.device.matched_output_device_id.empty()) {
-      params = audio_manager_->GetOutputStreamParameters(
-          out.device.matched_output_device_id);
-      MediaStreamDevice::AudioDeviceParameters& matched_output_params =
-          out.device.matched_output;
-      matched_output_params.sample_rate = params.sample_rate();
-      matched_output_params.channel_layout = params.channel_layout();
-      matched_output_params.frames_per_buffer = params.frames_per_buffer();
-    }
-  }
-
-  // Return the |session_id| through the listener by posting a task on
-  // IO thread since MediaStreamManager handles the callback asynchronously.
-  BrowserThread::PostTask(BrowserThread::IO,
-                          FROM_HERE,
-                          base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
-                                     this, session_id, out));
-}
-
-void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
-                                               const StreamDeviceInfo& info) {
+    const MediaStreamDevice& device,
+    base::TimeTicks start_time,
+    const media::AudioParameters& input_params,
+    const media::AudioParameters& matched_output_params,
+    const std::string& matched_output_device_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK_EQ(session_id, info.session_id);
   DCHECK(GetDevice(session_id) == devices_.end());
 
+  UMA_HISTOGRAM_TIMES("Media.AudioInputDeviceManager.OpenOnDeviceThreadTime",
+                      base::TimeTicks::Now() - start_time);
+
+  StreamDeviceInfo info(device.type, device.name, device.id);
+  info.session_id = session_id;
+  info.device.input.sample_rate = input_params.sample_rate();
+  info.device.input.channel_layout = input_params.channel_layout();
+  info.device.input.frames_per_buffer = input_params.frames_per_buffer();
+  info.device.input.effects = input_params.effects();
+  info.device.input.mic_positions = input_params.mic_positions();
+  info.device.matched_output_device_id = matched_output_device_id;
+  info.device.matched_output.sample_rate = matched_output_params.sample_rate();
+  info.device.matched_output.channel_layout =
+      matched_output_params.channel_layout();
+  info.device.matched_output.frames_per_buffer =
+      matched_output_params.frames_per_buffer();
+  info.device.matched_output.effects = matched_output_params.effects();
+
   devices_.push_back(info);
 
   for (auto& listener : listeners_)
@@ -220,9 +199,6 @@
     listener.Closed(stream_type, session_id);
 }
 
-bool AudioInputDeviceManager::IsOnDeviceThread() const {
-  return device_task_runner_->BelongsToCurrentThread();
-}
 
 AudioInputDeviceManager::StreamDeviceList::iterator
 AudioInputDeviceManager::GetDevice(int session_id) {
@@ -243,5 +219,4 @@
 }
 #endif
 
-
 }  // namespace content
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.h b/content/browser/renderer_host/media/audio_input_device_manager.h
index 25405148..b99b467d 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager.h
+++ b/content/browser/renderer_host/media/audio_input_device_manager.h
@@ -26,7 +26,7 @@
 #include "content/public/common/media_stream_request.h"
 
 namespace media {
-class AudioManager;
+class AudioSystem;
 }
 
 namespace content {
@@ -40,7 +40,7 @@
   // TODO(xians): Remove it when the webrtc unittest does not need it any more.
   static const int kFakeOpenSessionId;
 
-  explicit AudioInputDeviceManager(media::AudioManager* audio_manager);
+  explicit AudioInputDeviceManager(media::AudioSystem* audio_system);
 
   // Gets the opened device info by |session_id|. Returns NULL if the device
   // is not opened, otherwise the opened device. Called on IO thread.
@@ -66,19 +66,18 @@
   typedef std::vector<StreamDeviceInfo> StreamDeviceList;
   ~AudioInputDeviceManager() override;
 
-  // Opens the device on media stream device thread.
-  void OpenOnDeviceThread(int session_id, const MediaStreamDevice& device);
+  // Callback called on IO thread when device is opened.
+  void OpenedOnIOThread(int session_id,
+                        const MediaStreamDevice& device,
+                        base::TimeTicks start_time,
+                        const media::AudioParameters& input_params,
+                        const media::AudioParameters& matched_output_params,
+                        const std::string& matched_output_device_id);
 
-  // Callback used by OpenOnDeviceThread(), called with the session_id
-  // referencing the opened device on IO thread.
-  void OpenedOnIOThread(int session_id, const StreamDeviceInfo& info);
-  // Callback used by CloseOnDeviceThread(), called with the session_id
-  // referencing the closed device on IO thread.
+  // Callback called on IO thread with the session_id referencing the closed
+  // device.
   void ClosedOnIOThread(MediaStreamType type, int session_id);
 
-  // Verifies that the calling thread is media stream device thread.
-  bool IsOnDeviceThread() const;
-
   // Helper to return iterator to the device referenced by |session_id|. If no
   // device is found, it will return devices_.end().
   StreamDeviceList::iterator GetDevice(int session_id);
@@ -98,10 +97,7 @@
   int keyboard_mic_streams_count_;
 #endif
 
-  media::AudioManager* const audio_manager_;  // Weak.
-
-  // The message loop of media stream device thread that this object runs on.
-  scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
+  media::AudioSystem* const audio_system_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManager);
 };
diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
index bf3b80a5..2cb87419 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
@@ -21,7 +21,9 @@
 #include "content/public/common/media_stream_request.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_system_impl.h"
 #include "media/base/media_switches.h"
+#include "media/base/test_helpers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -58,18 +60,23 @@
 
 class MAYBE_AudioInputDeviceManagerTest : public testing::Test {
  public:
-  MAYBE_AudioInputDeviceManagerTest() {}
+  MAYBE_AudioInputDeviceManagerTest() : audio_thread_("AudioSystemThread") {
+    audio_thread_.StartAndWaitForTesting();
+  }
 
  protected:
   void SetUp() override {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseFakeDeviceForMediaStream);
-    audio_manager_ = media::AudioManager::CreateForTesting(
-        base::ThreadTaskRunnerHandle::Get());
+    // AudioInputDeviceManager accesses AudioSystem from IO thread, so it never
+    // runs on the same thread with it, even on Mac.
+    audio_manager_ =
+        media::AudioManager::CreateForTesting(audio_thread_.task_runner());
     // Flush the message loop to ensure proper initialization of AudioManager.
     base::RunLoop().RunUntilIdle();
 
-    manager_ = new AudioInputDeviceManager(audio_manager_.get());
+    audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
+    manager_ = new AudioInputDeviceManager(audio_system_.get());
     audio_input_listener_.reset(new MockAudioInputDeviceManagerListener());
     manager_->RegisterListener(audio_input_listener_.get());
 
@@ -85,12 +92,27 @@
 
   void TearDown() override {
     manager_->UnregisterListener(audio_input_listener_.get());
+    audio_system_.reset();
+    audio_manager_.reset();
+    audio_thread_.Stop();
+  }
+
+  void WaitForOpenCompletion() {
+    media::WaitableMessageLoopEvent event;
+    audio_thread_.task_runner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&base::DoNothing), event.GetClosure());
+    // Runs the loop and waits for the |audio_thread_| to call event's
+    // closure.
+    event.RunAndWait();
+    base::RunLoop().RunUntilIdle();
   }
 
   TestBrowserThreadBundle thread_bundle_;
+  base::Thread audio_thread_;
+  media::ScopedAudioManagerPtr audio_manager_;
+  std::unique_ptr<media::AudioSystem> audio_system_;
   scoped_refptr<AudioInputDeviceManager> manager_;
   std::unique_ptr<MockAudioInputDeviceManagerListener> audio_input_listener_;
-  media::ScopedAudioManagerPtr audio_manager_;
   StreamDeviceInfoArray devices_;
 
  private:
@@ -113,7 +135,7 @@
                 Opened(MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
         .Times(1);
     // Waits for the callback.
-    base::RunLoop().RunUntilIdle();
+    WaitForOpenCompletion();
 
     manager_->Close(session_id);
     EXPECT_CALL(*audio_input_listener_,
@@ -146,7 +168,7 @@
         .Times(1);
 
     // Waits for the callback.
-    base::RunLoop().RunUntilIdle();
+    WaitForOpenCompletion();
   }
 
   // Checks if the session_ids are unique.
@@ -183,7 +205,7 @@
       .Times(1);
 
   // Waits for the callback.
-  base::RunLoop().RunUntilIdle();
+  WaitForOpenCompletion();
 }
 
 // Opens default device twice.
@@ -205,7 +227,7 @@
               Opened(MEDIA_DEVICE_AUDIO_CAPTURE, second_session_id))
       .Times(1);
   // Waits for the callback.
-  base::RunLoop().RunUntilIdle();
+  WaitForOpenCompletion();
 
   manager_->Close(first_session_id);
   manager_->Close(second_session_id);
@@ -238,7 +260,7 @@
     EXPECT_CALL(*audio_input_listener_,
                 Opened(MEDIA_DEVICE_AUDIO_CAPTURE, session_id[index]))
         .Times(1);
-    base::RunLoop().RunUntilIdle();
+    WaitForOpenCompletion();
 
     const StreamDeviceInfo* info = manager_->GetOpenedDeviceInfoById(
         session_id[index]);
@@ -262,7 +284,7 @@
   EXPECT_CALL(*audio_input_listener_,
               Opened(MEDIA_DEVICE_AUDIO_CAPTURE, session_id))
       .Times(1);
-  base::RunLoop().RunUntilIdle();
+  WaitForOpenCompletion();
 
   // Access a non-opened device.
   // This should fail and return an empty StreamDeviceInfo.
diff --git a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
index a4cdfed..6ec80cf 100644
--- a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
+++ b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_IN_PROCESS_BUILDABLE_VIDEO_CAPTURE_DEVICE_H_
 
 #include "content/browser/renderer_host/media/video_capture_controller.h"
+#include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_device_client.h"
diff --git a/content/browser/renderer_host/media/in_process_video_capture_provider.cc b/content/browser/renderer_host/media/in_process_video_capture_provider.cc
new file mode 100644
index 0000000..4c60e63
--- /dev/null
+++ b/content/browser/renderer_host/media/in_process_video_capture_provider.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/media/in_process_video_capture_provider.h"
+
+#include "content/browser/renderer_host/media/in_process_buildable_video_capture_device.h"
+
+namespace content {
+
+InProcessVideoCaptureProvider::InProcessVideoCaptureProvider(
+    std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
+    scoped_refptr<base::SingleThreadTaskRunner> device_task_runner)
+    : video_capture_system_(std::move(video_capture_system)),
+      device_task_runner_(std::move(device_task_runner)) {}
+
+InProcessVideoCaptureProvider::~InProcessVideoCaptureProvider() = default;
+
+void InProcessVideoCaptureProvider::GetDeviceInfosAsync(
+    const base::Callback<void(
+        const std::vector<media::VideoCaptureDeviceInfo>&)>& result_callback) {
+  // Using Unretained() is safe because |this| owns |video_capture_system_| and
+  // |result_callback| has ownership of |this|.
+  device_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&media::VideoCaptureSystem::GetDeviceInfosAsync,
+                            base::Unretained(video_capture_system_.get()),
+                            result_callback));
+}
+
+std::unique_ptr<BuildableVideoCaptureDevice>
+InProcessVideoCaptureProvider::CreateBuildableDevice(
+    const std::string& device_id,
+    MediaStreamType stream_type) {
+  return base::MakeUnique<InProcessBuildableVideoCaptureDevice>(
+      device_task_runner_, video_capture_system_.get());
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/media/in_process_video_capture_provider.h b/content/browser/renderer_host/media/in_process_video_capture_provider.h
new file mode 100644
index 0000000..5777613
--- /dev/null
+++ b/content/browser/renderer_host/media/in_process_video_capture_provider.h
@@ -0,0 +1,40 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_MEDIA_IN_PROCESS_VIDEO_CAPTURE_SYSTEM_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_IN_PROCESS_VIDEO_CAPTURE_SYSTEM_H_
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/renderer_host/media/video_capture_provider.h"
+#include "media/capture/video/video_capture_system.h"
+
+namespace content {
+
+class CONTENT_EXPORT InProcessVideoCaptureProvider
+    : public VideoCaptureProvider {
+ public:
+  InProcessVideoCaptureProvider(
+      std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
+      scoped_refptr<base::SingleThreadTaskRunner> device_task_runner);
+  ~InProcessVideoCaptureProvider() override;
+
+  void GetDeviceInfosAsync(
+      const base::Callback<void(
+          const std::vector<media::VideoCaptureDeviceInfo>&)>& result_callback)
+      override;
+
+  std::unique_ptr<BuildableVideoCaptureDevice> CreateBuildableDevice(
+      const std::string& device_id,
+      MediaStreamType stream_type) override;
+
+ private:
+  const std::unique_ptr<media::VideoCaptureSystem> video_capture_system_;
+  // The message loop of media stream device thread, where VCD's live.
+  scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_IN_PROCESS_VIDEO_CAPTURE_SYSTEM_H_
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index 2a28c62..1c94874 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -407,10 +407,13 @@
     const std::string& device_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   media::VideoCaptureFormats formats;
-  media_stream_manager_->video_capture_manager()->GetDeviceFormatsInUse(
-      MEDIA_DEVICE_VIDEO_CAPTURE, device_id, &formats);
-  if (!formats.empty())
+  base::Optional<media::VideoCaptureFormat> format =
+      media_stream_manager_->video_capture_manager()->GetDeviceFormatInUse(
+          MEDIA_DEVICE_VIDEO_CAPTURE, device_id);
+  if (format.has_value()) {
+    formats.push_back(format.value());
     return formats;
+  }
 
   media_stream_manager_->video_capture_manager()->GetDeviceSupportedFormats(
       device_id, &formats);
diff --git a/content/browser/renderer_host/media/media_devices_manager_unittest.cc b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
index cbad64f..fbc68f28 100644
--- a/content/browser/renderer_host/media/media_devices_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/renderer_host/media/in_process_video_capture_provider.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/audio/audio_device_name.h"
@@ -148,8 +149,12 @@
     video_capture_device_factory_ = video_capture_device_factory.get();
     auto video_capture_system = base::MakeUnique<media::VideoCaptureSystemImpl>(
         std::move(video_capture_device_factory));
-    video_capture_manager_ = new VideoCaptureManager(
-        std::move(video_capture_system), base::ThreadTaskRunnerHandle::Get());
+    auto video_capture_provider =
+        base::MakeUnique<InProcessVideoCaptureProvider>(
+            std::move(video_capture_system),
+            base::ThreadTaskRunnerHandle::Get());
+    video_capture_manager_ =
+        new VideoCaptureManager(std::move(video_capture_provider));
     media_devices_manager_.reset(new MediaDevicesManager(
         audio_system_.get(), video_capture_manager_, nullptr));
   }
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index 5d10dc88..f91700b 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -237,16 +237,17 @@
            const MediaStreamUIProxy::WindowIdCallback& window_id_callback));
 };
 
-class MockVideoCaptureSystem : public media::VideoCaptureSystem {
+class MockVideoCaptureProvider : public VideoCaptureProvider {
  public:
   MOCK_METHOD1(GetDeviceInfosAsync,
                void(const base::Callback<
                     void(const std::vector<media::VideoCaptureDeviceInfo>&)>&
                         result_callback));
 
-  MOCK_METHOD1(
-      CreateDevice,
-      std::unique_ptr<media::VideoCaptureDevice>(const std::string& device_id));
+  MOCK_METHOD2(CreateBuildableDevice,
+               std::unique_ptr<BuildableVideoCaptureDevice>(
+                   const std::string& device_id,
+                   MediaStreamType stream_type));
 };
 
 class MediaStreamDispatcherHostTest : public testing::Test {
@@ -261,12 +262,12 @@
     // Make sure we use fake devices to avoid long delays.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseFakeDeviceForMediaStream);
-    auto mock_video_capture_system = base::MakeUnique<MockVideoCaptureSystem>();
-    mock_video_capture_system_ = mock_video_capture_system.get();
+    auto mock_video_capture_provider =
+        base::MakeUnique<MockVideoCaptureProvider>();
+    mock_video_capture_provider_ = mock_video_capture_provider.get();
     // Create our own MediaStreamManager.
     media_stream_manager_ = base::MakeUnique<MediaStreamManager>(
-        audio_system_.get(), std::move(mock_video_capture_system),
-        base::ThreadTaskRunnerHandle::Get());
+        audio_system_.get(), std::move(mock_video_capture_provider));
 
     MockResourceContext* mock_resource_context =
         static_cast<MockResourceContext*>(
@@ -295,7 +296,7 @@
   void SetUp() override {
     stub_video_device_ids_.emplace_back(kRegularVideoDeviceId);
     stub_video_device_ids_.emplace_back(kDepthVideoDeviceId);
-    ON_CALL(*mock_video_capture_system_, GetDeviceInfosAsync(_))
+    ON_CALL(*mock_video_capture_provider_, GetDeviceInfosAsync(_))
         .WillByDefault(Invoke(
             [this](const base::Callback<void(
                        const std::vector<media::VideoCaptureDeviceInfo>&)>&
@@ -473,7 +474,7 @@
   media::AudioDeviceDescriptions audio_device_descriptions_;
   std::vector<std::string> stub_video_device_ids_;
   url::Origin origin_;
-  MockVideoCaptureSystem* mock_video_capture_system_;
+  MockVideoCaptureProvider* mock_video_capture_provider_;
 };
 
 TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithVideoOnly) {
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 2711aac7..5818cef 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -28,6 +28,7 @@
 #include "build/build_config.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
+#include "content/browser/renderer_host/media/in_process_video_capture_provider.h"
 #include "content/browser/renderer_host/media/media_capture_devices_impl.h"
 #include "content/browser/renderer_host/media/media_devices_manager.h"
 #include "content/browser/renderer_host/media/media_stream_requester.h"
@@ -394,12 +395,11 @@
 }
 
 MediaStreamManager::MediaStreamManager(media::AudioSystem* audio_system)
-    : MediaStreamManager(audio_system, nullptr, nullptr) {}
+    : MediaStreamManager(audio_system, nullptr) {}
 
 MediaStreamManager::MediaStreamManager(
     media::AudioSystem* audio_system,
-    std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
-    scoped_refptr<base::SingleThreadTaskRunner> device_task_runner)
+    std::unique_ptr<VideoCaptureProvider> video_capture_provider)
     : audio_system_(audio_system),
 #if defined(OS_WIN)
       video_capture_thread_("VideoCaptureThread"),
@@ -408,13 +408,9 @@
           switches::kUseFakeUIForMediaStream)) {
   DCHECK(audio_system_);
 
-  if (!video_capture_system) {
-    video_capture_system = base::MakeUnique<media::VideoCaptureSystemImpl>(
-        media::VideoCaptureDeviceFactory::CreateFactory(
-            BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)));
-  }
-  if (!device_task_runner) {
-    device_task_runner = audio_system_->GetTaskRunner();
+  if (!video_capture_provider) {
+    scoped_refptr<base::SingleThreadTaskRunner> device_task_runner =
+        audio_system_->GetTaskRunner();
 #if defined(OS_WIN)
     // Use an STA Video Capture Thread to try to avoid crashes on enumeration of
     // buggy third party Direct Show modules, http://crbug.com/428958.
@@ -422,9 +418,13 @@
     CHECK(video_capture_thread_.Start());
     device_task_runner = video_capture_thread_.task_runner();
 #endif
+    video_capture_provider = base::MakeUnique<InProcessVideoCaptureProvider>(
+        base::MakeUnique<media::VideoCaptureSystemImpl>(
+            media::VideoCaptureDeviceFactory::CreateFactory(
+                BrowserThread::GetTaskRunnerForThread(BrowserThread::UI))),
+        std::move(device_task_runner));
   }
-  InitializeMaybeAsync(std::move(video_capture_system),
-                       std::move(device_task_runner));
+  InitializeMaybeAsync(std::move(video_capture_provider));
 
   base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
   // BrowserMainLoop always creates the PowerMonitor instance before creating
@@ -1229,8 +1229,7 @@
 }
 
 void MediaStreamManager::InitializeMaybeAsync(
-    std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
-    scoped_refptr<base::SingleThreadTaskRunner> device_task_runner) {
+    std::unique_ptr<VideoCaptureProvider> video_capture_provider) {
   // Some unit tests initialize the MSM in the IO thread and assume the
   // initialization is done synchronously. Other clients call this from a
   // different thread and expect initialization to run asynchronously.
@@ -1238,8 +1237,8 @@
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&MediaStreamManager::InitializeMaybeAsync,
-                   base::Unretained(this), base::Passed(&video_capture_system),
-                   std::move(device_task_runner)));
+                   base::Unretained(this),
+                   base::Passed(&video_capture_provider)));
     return;
   }
 
@@ -1261,8 +1260,7 @@
   tracked_objects::ScopedTracker tracking_profile2(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 2"));
-  audio_input_device_manager_ =
-      new AudioInputDeviceManager(audio_system_->GetAudioManager());
+  audio_input_device_manager_ = new AudioInputDeviceManager(audio_system_);
   audio_input_device_manager_->RegisterListener(this);
 
   // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
@@ -1280,8 +1278,8 @@
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 4"));
 
-  video_capture_manager_ = new VideoCaptureManager(
-      std::move(video_capture_system), std::move(device_task_runner));
+  video_capture_manager_ =
+      new VideoCaptureManager(std::move(video_capture_provider));
   video_capture_manager_->RegisterListener(this);
 
   media_devices_manager_.reset(
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index b3a5cec..e4d488e 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -49,7 +49,6 @@
 
 namespace media {
 class AudioSystem;
-class VideoCaptureSystem;
 }
 
 namespace url {
@@ -63,6 +62,7 @@
 class MediaStreamRequester;
 class MediaStreamUIProxy;
 class VideoCaptureManager;
+class VideoCaptureProvider;
 
 // MediaStreamManager is used to generate and close new media devices, not to
 // start the media flow. The classes requesting new media streams are answered
@@ -92,8 +92,7 @@
   // |video_capture_system| or |device_task_runner| are null.
   explicit MediaStreamManager(
       media::AudioSystem* audio_system,
-      std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
-      scoped_refptr<base::SingleThreadTaskRunner> device_task_runner);
+      std::unique_ptr<VideoCaptureProvider> video_capture_provider);
 
   ~MediaStreamManager() override;
 
@@ -282,8 +281,7 @@
   using DeviceRequests = std::list<LabeledDeviceRequest>;
 
   void InitializeMaybeAsync(
-      std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
-      scoped_refptr<base::SingleThreadTaskRunner> device_task_runner);
+      std::unique_ptr<VideoCaptureProvider> video_capture_provider);
 
   // |output_parameters| contains real values only if the request requires it.
   void HandleAccessRequestResponse(
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index db89a056..22578ac1 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -371,8 +371,8 @@
                                   consumer_resource_utilization);
 }
 
-const media::VideoCaptureFormat& VideoCaptureController::GetVideoCaptureFormat()
-    const {
+const base::Optional<media::VideoCaptureFormat>
+VideoCaptureController::GetVideoCaptureFormat() const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   return video_capture_format_;
 }
@@ -449,10 +449,12 @@
                                frame_info->coded_size.width(),
                                frame_info->coded_size.height());
     double frame_rate = 0.0f;
-    media::VideoFrameMetadata metadata;
-    metadata.MergeInternalValuesFrom(*frame_info->metadata);
-    if (!metadata.GetDouble(VideoFrameMetadata::FRAME_RATE, &frame_rate)) {
-      frame_rate = video_capture_format_.frame_rate;
+    if (video_capture_format_) {
+      media::VideoFrameMetadata metadata;
+      metadata.MergeInternalValuesFrom(*frame_info->metadata);
+      if (!metadata.GetDouble(VideoFrameMetadata::FRAME_RATE, &frame_rate)) {
+        frame_rate = video_capture_format_->frame_rate;
+      }
     }
     UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate", frame_rate);
     has_received_frames_ = true;
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index 470bba6..a75de3c2 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -13,8 +13,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/process/process.h"
-#include "content/browser/renderer_host/media/buildable_video_capture_device.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
+#include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/common/content_export.h"
 #include "content/common/media/video_capture.h"
 #include "content/public/common/media_stream_request.h"
@@ -97,7 +97,7 @@
                     int buffer_id,
                     double consumer_resource_utilization);
 
-  const media::VideoCaptureFormat& GetVideoCaptureFormat() const;
+  const base::Optional<media::VideoCaptureFormat> GetVideoCaptureFormat() const;
 
   bool has_received_frames() const { return has_received_frames_; }
 
@@ -241,7 +241,7 @@
   // True if the controller has received a video frame from the device.
   bool has_received_frames_;
 
-  media::VideoCaptureFormat video_capture_format_;
+  base::Optional<media::VideoCaptureFormat> video_capture_format_;
 
   base::WeakPtrFactory<VideoCaptureController> weak_ptr_factory_;
 
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index 9c58c0f..169daaa8 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -25,7 +25,6 @@
 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
 #include "content/browser/media/capture/web_contents_video_capture_device.h"
 #include "content/browser/media/media_internals.h"
-#include "content/browser/renderer_host/media/in_process_buildable_video_capture_device.h"
 #include "content/browser/renderer_host/media/video_capture_controller.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
 #include "content/public/browser/browser_thread.h"
@@ -104,11 +103,9 @@
     : controller_(controller), session_id_(session_id), params_(params) {}
 
 VideoCaptureManager::VideoCaptureManager(
-    std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
-    scoped_refptr<base::SingleThreadTaskRunner> device_task_runner)
-    : device_task_runner_(std::move(device_task_runner)),
-      new_capture_session_id_(1),
-      video_capture_system_(std::move(video_capture_system)) {}
+    std::unique_ptr<VideoCaptureProvider> video_capture_provider)
+    : new_capture_session_id_(1),
+      video_capture_provider_(std::move(video_capture_provider)) {}
 
 VideoCaptureManager::~VideoCaptureManager() {
   DCHECK(controllers_.empty());
@@ -131,7 +128,6 @@
     MediaStreamProviderListener* listener) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(listener);
-  DCHECK(device_task_runner_);
   listeners_.AddObserver(listener);
 #if defined(OS_ANDROID)
   application_state_has_running_activities_ = true;
@@ -152,17 +148,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "VideoCaptureManager::EnumerateDevices";
 
-  // OK to use base::Unretained(video_capture_system_) since we own the
-  // |video_capture_system_| and |this| is bound in
-  // |devices_enumerated_callback|.
-  device_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&media::VideoCaptureSystem::GetDeviceInfosAsync,
-                 base::Unretained(video_capture_system_.get()),
-                 // Pass a timer for UMA histogram collection.
-                 media::BindToCurrentLoop(base::Bind(
-                     &VideoCaptureManager::OnDeviceInfosReceived, this,
-                     base::Owned(new base::ElapsedTimer()), client_callback))));
+  // Pass a timer for UMA histogram collection.
+  video_capture_provider_->GetDeviceInfosAsync(media::BindToCurrentLoop(
+      base::Bind(&VideoCaptureManager::OnDeviceInfosReceived, this,
+                 base::Owned(new base::ElapsedTimer()), client_callback)));
 }
 
 int VideoCaptureManager::Open(const MediaStreamDevice& device) {
@@ -535,23 +524,22 @@
     return false;
   DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
 
-  return GetDeviceFormatsInUse(it->second.type, it->second.id, formats_in_use);
+  base::Optional<media::VideoCaptureFormat> format =
+      GetDeviceFormatInUse(it->second.type, it->second.id);
+  if (format.has_value())
+    formats_in_use->push_back(format.value());
+
+  return true;
 }
 
-bool VideoCaptureManager::GetDeviceFormatsInUse(
-    MediaStreamType stream_type,
-    const std::string& device_id,
-    media::VideoCaptureFormats* formats_in_use) {
+base::Optional<media::VideoCaptureFormat>
+VideoCaptureManager::GetDeviceFormatInUse(MediaStreamType stream_type,
+                                          const std::string& device_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(formats_in_use->empty());
-  // Return the currently in-use format(s) of the device, if it's started.
+  // Return the currently in-use format of the device, if it's started.
   VideoCaptureController* device_in_use =
       LookupControllerByMediaTypeAndDeviceId(stream_type, device_id);
-  if (device_in_use) {
-    // Currently only one format-in-use is supported at the VCC level.
-    formats_in_use->push_back(device_in_use->GetVideoCaptureFormat());
-  }
-  return true;
+  return device_in_use ? device_in_use->GetVideoCaptureFormat() : base::nullopt;
 }
 
 void VideoCaptureManager::SetDesktopCaptureWindowId(
@@ -798,10 +786,10 @@
     return existing_device;
   }
 
-  VideoCaptureController* new_controller = new VideoCaptureController(
-      device_info.id, device_info.type, params,
-      base::MakeUnique<InProcessBuildableVideoCaptureDevice>(
-          device_task_runner_, video_capture_system_.get()));
+  VideoCaptureController* new_controller =
+      new VideoCaptureController(device_info.id, device_info.type, params,
+                                 video_capture_provider_->CreateBuildableDevice(
+                                     device_info.id, device_info.type));
   controllers_.emplace_back(new_controller);
   return new_controller;
 }
diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h
index 0021c1e..e79103fa 100644
--- a/content/browser/renderer_host/media/video_capture_manager.h
+++ b/content/browser/renderer_host/media/video_capture_manager.h
@@ -2,13 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// VideoCaptureManager is used to open/close, start/stop, enumerate available
-// video capture devices, and manage VideoCaptureController's.
-// All functions are expected to be called from Browser::IO thread. Some helper
-// functions (*OnDeviceThread) will dispatch operations to the device thread.
-// VideoCaptureManager will open OS dependent instances of VideoCaptureDevice.
-// A device can only be opened once.
-
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_
 
@@ -26,15 +19,14 @@
 #include "base/threading/thread_checker.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
-#include "content/browser/renderer_host/media/buildable_video_capture_device.h"
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
+#include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/common/content_export.h"
 #include "content/common/media/media_stream_options.h"
 #include "media/base/video_facing.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_device_info.h"
-#include "media/capture/video/video_capture_system.h"
 #include "media/capture/video_capture_types.h"
 
 #if defined(OS_ANDROID)
@@ -45,7 +37,11 @@
 class VideoCaptureController;
 class VideoCaptureControllerEventHandler;
 
-// VideoCaptureManager opens/closes and start/stops video capture devices.
+// VideoCaptureManager is used to open/close, start/stop, enumerate available
+// video capture devices, and manage VideoCaptureController's.
+// In its main usage in production, an instance is created by MediaStreamManager
+// on the Browser::IO thread. All public methods are expected to be called from
+// the Browser::IO thread. A device can only be opened once.
 class CONTENT_EXPORT VideoCaptureManager
     : public MediaStreamProvider,
       public BuildableVideoCaptureDevice::Callbacks {
@@ -56,9 +52,8 @@
   using DoneCB =
       base::Callback<void(const base::WeakPtr<VideoCaptureController>&)>;
 
-  VideoCaptureManager(
-      std::unique_ptr<media::VideoCaptureSystem> capture_system,
-      scoped_refptr<base::SingleThreadTaskRunner> device_task_runner);
+  explicit VideoCaptureManager(
+      std::unique_ptr<VideoCaptureProvider> video_capture_provider);
 
   // AddVideoCaptureObserver() can be called only before any devices are opened.
   // RemoveAllVideoCaptureObservers() can be called only after all devices
@@ -149,13 +144,12 @@
   // otherwise. |formats_in_use| is empty if the device is not in use.
   bool GetDeviceFormatsInUse(media::VideoCaptureSessionId capture_session_id,
                              media::VideoCaptureFormats* formats_in_use);
-  // Retrieves the format(s) currently in use.  Returns false if the
-  // |stream_type|, |device_id| pair is not found. Returns true and
-  // |formats_in_use| otherwise. |formats_in_use| is empty if the device is not
-  // in use.
-  bool GetDeviceFormatsInUse(MediaStreamType stream_type,
-                             const std::string& device_id,
-                             media::VideoCaptureFormats* supported_formats);
+  // Retrieves the format currently in use.  Returns base::nullopt if the
+  // |stream_type|, |device_id| pair is not found. Returns in-use format of the
+  // device otherwise.
+  base::Optional<media::VideoCaptureFormat> GetDeviceFormatInUse(
+      MediaStreamType stream_type,
+      const std::string& device_id);
 
   // Sets the platform-dependent window ID for the desktop capture notification
   // UI for the given session.
@@ -267,9 +261,6 @@
   bool application_state_has_running_activities_;
 #endif
 
-  // The message loop of media stream device thread, where VCD's live.
-  scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
-
   // Only accessed on Browser::IO thread.
   base::ObserverList<MediaStreamProviderListener> listeners_;
   media::VideoCaptureSessionId new_capture_session_id_;
@@ -292,9 +283,7 @@
   // bundles a session id integer and an associated photo-related request.
   std::list<std::pair<int, base::Closure>> photo_request_queue_;
 
-  // Device creation factory injected on construction from MediaStreamManager or
-  // from the test harness.
-  std::unique_ptr<media::VideoCaptureSystem> video_capture_system_;
+  const std::unique_ptr<VideoCaptureProvider> video_capture_provider_;
 
   base::ObserverList<media::VideoCaptureObserver> capture_observers_;
 
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index 86dccd3..2540b4a 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/renderer_host/media/in_process_video_capture_provider.h"
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
 #include "content/common/media/media_stream_options.h"
@@ -202,8 +203,11 @@
     video_capture_device_factory_ = video_capture_device_factory.get();
     auto video_capture_system = base::MakeUnique<media::VideoCaptureSystemImpl>(
         std::move(video_capture_device_factory));
-    vcm_ = new VideoCaptureManager(std::move(video_capture_system),
-                                   base::ThreadTaskRunnerHandle::Get());
+    auto video_capture_provider =
+        base::MakeUnique<InProcessVideoCaptureProvider>(
+            std::move(video_capture_system),
+            base::ThreadTaskRunnerHandle::Get());
+    vcm_ = new VideoCaptureManager(std::move(video_capture_provider));
     const int32_t kNumberOfFakeDevices = 2;
     video_capture_device_factory_->SetToDefaultDevicesConfig(
         kNumberOfFakeDevices);
@@ -608,34 +612,27 @@
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see no format in use.
-  media::VideoCaptureFormats formats_in_use;
-  EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id,
-                                          &formats_in_use));
-  EXPECT_TRUE(formats_in_use.empty());
+  EXPECT_EQ(base::nullopt,
+            vcm_->GetDeviceFormatInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id));
 
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
   base::RunLoop().RunUntilIdle();
-  // After StartClient(), |formats_in_use| should contain one valid format.
-  EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id,
-                                          &formats_in_use));
-  EXPECT_EQ(formats_in_use.size(), 1u);
-  if (formats_in_use.size()) {
-    media::VideoCaptureFormat& format_in_use = formats_in_use.front();
-    EXPECT_TRUE(format_in_use.IsValid());
-    EXPECT_GT(format_in_use.frame_size.width(), 1);
-    EXPECT_GT(format_in_use.frame_size.height(), 1);
-    EXPECT_GT(format_in_use.frame_rate, 1);
-  }
-  formats_in_use.clear();
+  // After StartClient(), device's format in use should be valid.
+  base::Optional<media::VideoCaptureFormat> format_in_use =
+      vcm_->GetDeviceFormatInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id);
+  EXPECT_TRUE(format_in_use.has_value());
+  EXPECT_TRUE(format_in_use->IsValid());
+  EXPECT_GT(format_in_use->frame_size.width(), 1);
+  EXPECT_GT(format_in_use->frame_size.height(), 1);
+  EXPECT_GT(format_in_use->frame_rate, 1);
 
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
   StopClient(client_id);
   base::RunLoop().RunUntilIdle();
-  // After StopClient(), the device's formats in use should be empty again.
-  EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id,
-                                          &formats_in_use));
-  EXPECT_TRUE(formats_in_use.empty());
+  // After StopClient(), the device's format in use should be empty again.
+  EXPECT_EQ(base::nullopt,
+            vcm_->GetDeviceFormatInUse(MEDIA_DEVICE_VIDEO_CAPTURE, device_id));
 
   vcm_->Close(video_session_id);
   base::RunLoop().RunUntilIdle();
diff --git a/content/browser/renderer_host/media/buildable_video_capture_device.h b/content/browser/renderer_host/media/video_capture_provider.h
similarity index 82%
rename from content/browser/renderer_host/media/buildable_video_capture_device.h
rename to content/browser/renderer_host/media/video_capture_provider.h
index e358435..8515a53 100644
--- a/content/browser/renderer_host/media/buildable_video_capture_device.h
+++ b/content/browser/renderer_host/media/video_capture_provider.h
@@ -9,6 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/capture/video/video_capture_device.h"
+#include "media/capture/video/video_capture_device_info.h"
 #include "media/capture/video_capture_types.h"
 
 namespace content {
@@ -65,6 +66,22 @@
                                               base::OnceClosure done_cb) = 0;
 };
 
+class CONTENT_EXPORT VideoCaptureProvider {
+ public:
+  virtual ~VideoCaptureProvider() {}
+
+  // The passed-in |result_callback| must guarantee that the called
+  // instance stays alive until |result_callback| is invoked.
+  virtual void GetDeviceInfosAsync(
+      const base::Callback<
+          void(const std::vector<media::VideoCaptureDeviceInfo>&)>&
+          result_callback) = 0;
+
+  virtual std::unique_ptr<BuildableVideoCaptureDevice> CreateBuildableDevice(
+      const std::string& device_id,
+      MediaStreamType stream_type) = 0;
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_BUILDABLE_VIDEO_CAPTURE_DEVICE_H_
diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc
index 5884c7bd..0acd84b 100644
--- a/content/browser/speech/speech_recognizer_impl_unittest.cc
+++ b/content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -92,6 +92,7 @@
 
   ~SpeechRecognizerImplTest() override {
     // Deleting |audio_manager_| on audio thread.
+    audio_system_.reset();
     audio_manager_.reset();
     audio_thread_.Stop();
   }
diff --git a/content/browser/tracing/background_tracing_config_impl.cc b/content/browser/tracing/background_tracing_config_impl.cc
index 3ce2fab..b5d5c9a 100644
--- a/content/browser/tracing/background_tracing_config_impl.cc
+++ b/content/browser/tracing/background_tracing_config_impl.cc
@@ -233,7 +233,7 @@
 
   for (const auto& it : *configs_list) {
     const base::DictionaryValue* config_dict = nullptr;
-    if (!it->GetAsDictionary(&config_dict))
+    if (!it.GetAsDictionary(&config_dict))
       return nullptr;
 
     config->AddPreemptiveRule(config_dict);
@@ -259,7 +259,7 @@
 
   for (const auto& it : *configs_list) {
     const base::DictionaryValue* config_dict = nullptr;
-    if (!it->GetAsDictionary(&config_dict))
+    if (!it.GetAsDictionary(&config_dict))
       return nullptr;
 
     std::string category_preset_string;
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index ad4bb8b..d22b6dd 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -250,7 +250,7 @@
       const base::DictionaryValue* dict;
       std::string kind;
       std::string device_id;
-      ASSERT_TRUE((*it)->GetAsDictionary(&dict));
+      ASSERT_TRUE(it->GetAsDictionary(&dict));
       ASSERT_TRUE(dict->GetString("kind", &kind));
       ASSERT_TRUE(dict->GetString("id", &device_id));
       ASSERT_FALSE(device_id.empty());
@@ -940,7 +940,7 @@
       const base::DictionaryValue* dict;
       std::string kind;
       std::string device_id;
-      ASSERT_TRUE((*it)->GetAsDictionary(&dict));
+      ASSERT_TRUE(it->GetAsDictionary(&dict));
       ASSERT_TRUE(dict->GetString("kind", &kind));
       ASSERT_TRUE(dict->GetString("id", &device_id));
       ASSERT_FALSE(device_id.empty());
diff --git a/content/browser/webrtc/webrtc_internals.cc b/content/browser/webrtc/webrtc_internals.cc
index 6f22232..376cb139 100644
--- a/content/browser/webrtc/webrtc_internals.cc
+++ b/content/browser/webrtc/webrtc_internals.cc
@@ -269,7 +269,7 @@
 
   // TODO(tommi): Consider removing all the peer_connection_data_.
   for (auto& dictionary : peer_connection_data_)
-    FreeLogList(dictionary.get());
+    FreeLogList(&dictionary);
 }
 
 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
@@ -278,7 +278,7 @@
     observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
 
   for (const auto& request : get_user_media_requests_) {
-    observer->OnUpdate("addGetUserMedia", request.get());
+    observer->OnUpdate("addGetUserMedia", &request);
   }
 }
 
diff --git a/content/browser/webrtc/webrtc_internals_unittest.cc b/content/browser/webrtc/webrtc_internals_unittest.cc
index d14eef08..bdadf0a 100644
--- a/content/browser/webrtc/webrtc_internals_unittest.cc
+++ b/content/browser/webrtc/webrtc_internals_unittest.cc
@@ -141,7 +141,7 @@
   ASSERT_TRUE(observer.value()->GetAsList(&list));
   EXPECT_EQ(1U, list->GetSize());
   base::DictionaryValue* dict = nullptr;
-  ASSERT_TRUE((*list->begin())->GetAsDictionary(&dict));
+  ASSERT_TRUE((*list->begin()).GetAsDictionary(&dict));
   base::ListValue* log = nullptr;
   ASSERT_FALSE(dict->GetList("log", &log));
 
@@ -167,7 +167,7 @@
   ASSERT_TRUE(observer.value()->GetAsList(&list));
   EXPECT_EQ(1U, list->GetSize());
   base::DictionaryValue* dict = nullptr;
-  ASSERT_TRUE((*list->begin())->GetAsDictionary(&dict));
+  ASSERT_TRUE((*list->begin()).GetAsDictionary(&dict));
   base::ListValue* log = nullptr;
   ASSERT_TRUE(dict->GetList("log", &log));
 
@@ -178,7 +178,7 @@
 
   ASSERT_TRUE(observer.value()->GetAsList(&list));
   EXPECT_EQ(1U, list->GetSize());
-  ASSERT_TRUE((*list->begin())->GetAsDictionary(&dict));
+  ASSERT_TRUE((*list->begin()).GetAsDictionary(&dict));
   ASSERT_FALSE(dict->GetList("log", &log));
 
   webrtc_internals.OnRemovePeerConnection(3, 4);
@@ -332,7 +332,7 @@
   EXPECT_EQ(1U, list->GetSize());
 
   base::DictionaryValue* dict = NULL;
-  EXPECT_TRUE((*list->begin())->GetAsDictionary(&dict));
+  EXPECT_TRUE((*list->begin()).GetAsDictionary(&dict));
 
   VerifyInt(dict, "rid", rid);
   VerifyInt(dict, "pid", pid);
@@ -345,7 +345,7 @@
   ASSERT_TRUE(dict->GetList("log", &log));
   EXPECT_EQ(1U, log->GetSize());
 
-  EXPECT_TRUE((*log->begin())->GetAsDictionary(&dict));
+  EXPECT_TRUE((*log->begin()).GetAsDictionary(&dict));
   VerifyString(dict, "type", update_type);
   VerifyString(dict, "value", update_value);
   std::string time;
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 707f34a..0787909 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -55,7 +55,7 @@
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
-#include "third_party/WebKit/public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "third_party/WebKit/public/platform/scheduler/child/webthread_base.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "ui/base/layout.h"
 #include "ui/events/gestures/blink/web_gesture_curve_impl.h"
@@ -69,7 +69,6 @@
 using blink::WebURL;
 using blink::WebURLError;
 using blink::WebURLLoader;
-using blink::scheduler::WebThreadImplForWorkerScheduler;
 
 namespace content {
 
@@ -400,8 +399,9 @@
 }
 
 blink::WebThread* BlinkPlatformImpl::CreateThread(const char* name) {
-  std::unique_ptr<WebThreadImplForWorkerScheduler> thread(
-      new WebThreadImplForWorkerScheduler(name));
+  std::unique_ptr<blink::scheduler::WebThreadBase> thread =
+      blink::scheduler::WebThreadBase::CreateWorkerThread(
+          name, base::Thread::Options());
   thread->Init();
   WaitUntilWebThreadTLSUpdate(thread.get());
   return thread.release();
diff --git a/content/public/renderer/BUILD.gn b/content/public/renderer/BUILD.gn
index 1c8cdc9..d03316b 100644
--- a/content/public/renderer/BUILD.gn
+++ b/content/public/renderer/BUILD.gn
@@ -60,6 +60,7 @@
     "render_view_visitor.h",
     "renderer_ppapi_host.h",
     "resource_fetcher.h",
+    "seccomp_sandbox_status_android.h",
     "video_encode_accelerator.cc",
     "video_encode_accelerator.h",
     "window_features_converter.cc",
diff --git a/content/public/renderer/seccomp_sandbox_status_android.h b/content/public/renderer/seccomp_sandbox_status_android.h
new file mode 100644
index 0000000..8a4d823
--- /dev/null
+++ b/content/public/renderer/seccomp_sandbox_status_android.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 CONTENT_PUBLIC_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
+#define CONTENT_PUBLIC_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+enum class SeccompSandboxStatus {
+  NOT_SUPPORTED = 0,  // Seccomp is not supported.
+  DETECTION_FAILED,   // Run-time detection of Seccomp+TSYNC failed.
+  FEATURE_DISABLED,   // Sandbox was disabled by FeatureList.
+  FEATURE_ENABLED,    // Sandbox was enabled by FeatureList.
+  ENGAGED,            // Sandbox was enabled and successfully turned on.
+  STATUS_MAX
+  // This enum is used by an UMA histogram, so only append values.
+};
+
+// Gets the SeccompSandboxStatus of the current process.
+CONTENT_EXPORT SeccompSandboxStatus GetSeccompSandboxStatus();
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 95a9f06c..27c88d7 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -343,6 +343,8 @@
     "scheduler/resource_dispatch_throttler.h",
     "screen_orientation/screen_orientation_dispatcher.cc",
     "screen_orientation/screen_orientation_dispatcher.h",
+    "seccomp_sandbox_status_android.cc",
+    "seccomp_sandbox_status_android.h",
     "service_worker/embedded_worker_devtools_agent.cc",
     "service_worker/embedded_worker_devtools_agent.h",
     "service_worker/embedded_worker_dispatcher.cc",
diff --git a/content/renderer/gpu/actions_parser.cc b/content/renderer/gpu/actions_parser.cc
index 650fd439..127c565f 100644
--- a/content/renderer/gpu/actions_parser.cc
+++ b/content/renderer/gpu/actions_parser.cc
@@ -71,7 +71,7 @@
 
   for (const auto& pointer_value : *pointer_list) {
     const base::DictionaryValue* pointer_actions;
-    if (!pointer_value->GetAsDictionary(&pointer_actions)) {
+    if (!pointer_value.GetAsDictionary(&pointer_actions)) {
       error_message_ =
           base::StringPrintf("pointer actions is missing or not a dictionary");
       return false;
@@ -157,7 +157,7 @@
   SyntheticPointerActionListParams::ParamList param_list;
   for (const auto& action_value : actions) {
     const base::DictionaryValue* action;
-    if (!action_value->GetAsDictionary(&action)) {
+    if (!action_value.GetAsDictionary(&action)) {
       error_message_ = base::StringPrintf(
           "actions[%d].actions is missing or not a dictionary", action_index_);
       return false;
diff --git a/content/renderer/media/media_interface_provider.cc b/content/renderer/media/media_interface_provider.cc
index 8f779f5b..49592bb8 100644
--- a/content/renderer/media/media_interface_provider.cc
+++ b/content/renderer/media/media_interface_provider.cc
@@ -16,16 +16,26 @@
 
 MediaInterfaceProvider::MediaInterfaceProvider(
     service_manager::InterfaceProvider* remote_interfaces)
-    : remote_interfaces_(remote_interfaces) {}
+    : remote_interfaces_(remote_interfaces), weak_factory_(this) {
+  task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  weak_this_ = weak_factory_.GetWeakPtr();
+}
 
 MediaInterfaceProvider::~MediaInterfaceProvider() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(task_runner_->BelongsToCurrentThread());
 }
 
 void MediaInterfaceProvider::GetInterface(const std::string& interface_name,
                                           mojo::ScopedMessagePipeHandle pipe) {
   DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::Bind(&MediaInterfaceProvider::GetInterface, weak_this_,
+                              interface_name, base::Passed(&pipe)));
+    return;
+  }
+
+  DCHECK(task_runner_->BelongsToCurrentThread());
 
   if (interface_name == media::mojom::ContentDecryptionModule::Name_) {
     GetMediaInterfaceFactory()->CreateCdm(
@@ -49,7 +59,7 @@
 media::mojom::InterfaceFactory*
 MediaInterfaceProvider::GetMediaInterfaceFactory() {
   DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(task_runner_->BelongsToCurrentThread());
 
   if (!media_interface_factory_) {
     remote_interfaces_->GetInterface(&media_interface_factory_);
@@ -62,7 +72,7 @@
 
 void MediaInterfaceProvider::OnConnectionError() {
   DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(task_runner_->BelongsToCurrentThread());
 
   media_interface_factory_.reset();
 }
diff --git a/content/renderer/media/media_interface_provider.h b/content/renderer/media/media_interface_provider.h
index d761fdf7..532bb4ca 100644
--- a/content/renderer/media/media_interface_provider.h
+++ b/content/renderer/media/media_interface_provider.h
@@ -7,7 +7,8 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/threading/thread_checker.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
 #include "media/mojo/interfaces/interface_factory.mojom.h"
 #include "services/service_manager/public/interfaces/interface_provider.mojom.h"
@@ -21,7 +22,7 @@
 
 // MediaInterfaceProvider is an implementation of mojo InterfaceProvider that
 // provides media related services and handles disconnection automatically.
-// This class is single threaded.
+// The GetInterface can be called on any thread.
 class CONTENT_EXPORT MediaInterfaceProvider
     : public service_manager::mojom::InterfaceProvider {
  public:
@@ -37,10 +38,13 @@
   media::mojom::InterfaceFactory* GetMediaInterfaceFactory();
   void OnConnectionError();
 
-  base::ThreadChecker thread_checker_;
   service_manager::InterfaceProvider* remote_interfaces_;
   media::mojom::InterfaceFactoryPtr media_interface_factory_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtr<MediaInterfaceProvider> weak_this_;
+  base::WeakPtrFactory<MediaInterfaceProvider> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaInterfaceProvider);
 };
 
diff --git a/content/renderer/media/media_stream_video_track.cc b/content/renderer/media/media_stream_video_track.cc
index 143b0c7..d5d966d 100644
--- a/content/renderer/media/media_stream_video_track.cc
+++ b/content/renderer/media/media_stream_video_track.cc
@@ -390,16 +390,21 @@
 
 void MediaStreamVideoTrack::GetSettings(
     blink::WebMediaStreamTrack::Settings& settings) {
+  DCHECK(main_render_thread_checker_.CalledOnValidThread());
+  if (width_ && height_) {
+    settings.width = width_;
+    settings.height = height_;
+  }
+
+  if (!source_)
+    return;
+
   base::Optional<media::VideoCaptureFormat> format =
       source_->GetCurrentFormat();
   if (format) {
     settings.frame_rate = format->frame_rate;
     settings.video_kind = GetVideoKindForFormat(*format);
   }
-  if (width_ && height_) {
-    settings.width = width_;
-    settings.height = height_;
-  }
   switch (source_->device_info().device.video_facing) {
     case media::MEDIA_VIDEO_FACING_NONE:
       settings.facing_mode = blink::WebMediaStreamTrack::FacingMode::kNone;
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index 100cb53..cb5d7bea 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -31,6 +31,7 @@
 #include "content/renderer/media/webrtc_logging.h"
 #include "content/renderer/media/webrtc_uma_histograms.h"
 #include "content/renderer/render_thread_impl.h"
+#include "media/audio/audio_device_description.h"
 #include "media/capture/video_capture_types.h"
 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
@@ -85,7 +86,7 @@
                   const MediaDeviceInfoArray& device_infos,
                   std::string* device_id) {
   DCHECK(!constraints.IsNull());
-  DCHECK(device_id->empty());
+  DCHECK(media::AudioDeviceDescription::IsDefaultDevice(*device_id));
 
   if (constraints.Basic().device_id.Exact().size() > 1) {
     LOG(ERROR) << "Only one required device ID is supported";
@@ -398,6 +399,12 @@
         current_request_info_->request().AudioConstraints(),
         &current_request_info_->stream_controls()->audio,
         &request_audio_input_devices);
+    // Explicitly initialize the requested device ID to the default.
+    if (IsDeviceSource(
+            current_request_info_->stream_controls()->audio.stream_source)) {
+      current_request_info_->stream_controls()->audio.device_id =
+          std::string(media::AudioDeviceDescription::kDefaultDeviceId);
+    }
     CopyHotwordAndLocalEchoToStreamControls(
         current_request_info_->request().AudioConstraints(),
         current_request_info_->stream_controls());
diff --git a/content/renderer/media/user_media_client_impl_unittest.cc b/content/renderer/media/user_media_client_impl_unittest.cc
index 9735d97..f01ef2d 100644
--- a/content/renderer/media/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/user_media_client_impl_unittest.cc
@@ -22,6 +22,7 @@
 #include "content/renderer/media/mock_media_stream_dispatcher.h"
 #include "content/renderer/media/mock_media_stream_video_source.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
+#include "media/audio/audio_device_description.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
@@ -900,11 +901,12 @@
       CreateDeviceConstraints(nullptr, kInvalidDeviceId, kInvalidDeviceId);
   blink::WebMediaConstraints video_constraints =
       CreateDeviceConstraints(nullptr, kInvalidDeviceId, kInvalidDeviceId);
-  // MockMediaStreamDispatcher uses empty string as default audio device ID.
   // MockMediaDevicesDispatcher uses the first device in the enumeration as
   // default video device ID.
-  TestValidRequestWithConstraints(audio_constraints, video_constraints,
-                                  std::string(), kFakeVideoInputDeviceId1);
+  TestValidRequestWithConstraints(
+      audio_constraints, video_constraints,
+      std::string(media::AudioDeviceDescription::kDefaultDeviceId),
+      kFakeVideoInputDeviceId1);
 }
 
 TEST_F(UserMediaClientImplTest, CreateWithFacingModeUser) {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index d1c054f..6c478d38 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -156,7 +156,7 @@
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebThread.h"
 #include "third_party/WebKit/public/platform/scheduler/child/compositor_worker_scheduler.h"
-#include "third_party/WebKit/public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "third_party/WebKit/public/platform/scheduler/child/webthread_base.h"
 #include "third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h"
 #include "third_party/WebKit/public/web/WebDatabase.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -226,7 +226,6 @@
 using blink::WebSecurityPolicy;
 using blink::WebString;
 using blink::WebView;
-using blink::scheduler::WebThreadImplForWorkerScheduler;
 
 namespace content {
 
@@ -290,25 +289,6 @@
         blink::kWebMemoryPressureLevelCritical,
     "blink::WebMemoryPressureLevelCritical not align");
 
-class WebThreadForCompositor : public WebThreadImplForWorkerScheduler {
- public:
-  explicit WebThreadForCompositor(base::Thread::Options options)
-      : WebThreadImplForWorkerScheduler("Compositor", options) {
-    Init();
-  }
-  ~WebThreadForCompositor() override {}
-
- private:
-  // WebThreadImplForWorkerScheduler:
-  std::unique_ptr<blink::scheduler::WorkerScheduler> CreateWorkerScheduler()
-      override {
-    return base::MakeUnique<blink::scheduler::CompositorWorkerScheduler>(
-        GetThread());
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(WebThreadForCompositor);
-};
-
 void* CreateHistogram(
     const char *name, int min, int max, size_t buckets) {
   if (min <= 0)
@@ -1087,7 +1067,8 @@
 #if defined(OS_ANDROID)
   options.priority = base::ThreadPriority::DISPLAY;
 #endif
-  compositor_thread_.reset(new WebThreadForCompositor(options));
+  compositor_thread_ =
+      blink::scheduler::WebThreadBase::CreateCompositorThread(options);
   blink_platform_impl_->SetCompositorThread(compositor_thread_.get());
   compositor_task_runner_ = compositor_thread_->GetTaskRunner();
   compositor_task_runner_->PostTask(
diff --git a/content/renderer/renderer_main_platform_delegate_android.cc b/content/renderer/renderer_main_platform_delegate_android.cc
index 5341528f..a2ece9e 100644
--- a/content/renderer/renderer_main_platform_delegate_android.cc
+++ b/content/renderer/renderer_main_platform_delegate_android.cc
@@ -16,6 +16,7 @@
 #if BUILDFLAG(USE_SECCOMP_BPF)
 #include "content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h"
 #include "content/public/common/content_features.h"
+#include "content/renderer/seccomp_sandbox_status_android.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #endif
 
@@ -26,27 +27,21 @@
 // Scoper class to record a SeccompSandboxStatus UMA value.
 class RecordSeccompStatus {
  public:
-  enum SeccompSandboxStatus {
-    NOT_SUPPORTED = 0,  // Seccomp is not supported.
-    DETECTION_FAILED,   // Run-time detection of Seccomp+TSYNC failed.
-    FEATURE_DISABLED,   // Sandbox was disabled by FeatureList.
-    FEATURE_ENABLED,    // Sandbox was enabled by FeatureList.
-    ENGAGED,            // Sandbox was enabled and successfully turned on.
-    STATUS_MAX
-    // This enum is used by an UMA histogram, so only append values.
-  };
-
-  RecordSeccompStatus() : status_(NOT_SUPPORTED) {}
-
-  ~RecordSeccompStatus() {
-    UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.RendererSandbox", status_,
-                              STATUS_MAX);
+  RecordSeccompStatus() {
+    SetSeccompSandboxStatus(SeccompSandboxStatus::NOT_SUPPORTED);
   }
 
-  void set_status(SeccompSandboxStatus status) { status_ = status; }
+  ~RecordSeccompStatus() {
+    UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.RendererSandbox",
+                              GetSeccompSandboxStatus(),
+                              SeccompSandboxStatus::STATUS_MAX);
+  }
+
+  void set_status(SeccompSandboxStatus status) {
+    SetSeccompSandboxStatus(status);
+  }
 
  private:
-  SeccompSandboxStatus status_;
   DISALLOW_COPY_AND_ASSIGN(RecordSeccompStatus);
 };
 
@@ -103,7 +98,7 @@
   // Do run-time detection to ensure that support is present.
   if (!sandbox::SandboxBPF::SupportsSeccompSandbox(
           sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED)) {
-    status_uma.set_status(RecordSeccompStatus::DETECTION_FAILED);
+    status_uma.set_status(SeccompSandboxStatus::DETECTION_FAILED);
     LOG(WARNING) << "Seccomp support should be present, but detection "
         << "failed. Continuing without Seccomp-BPF.";
     return true;
@@ -111,7 +106,7 @@
 
   // Seccomp has been detected, check if the field trial experiment should run.
   if (base::FeatureList::IsEnabled(features::kSeccompSandboxAndroid)) {
-    status_uma.set_status(RecordSeccompStatus::FEATURE_ENABLED);
+    status_uma.set_status(SeccompSandboxStatus::FEATURE_ENABLED);
 
     // TODO(rsesek): When "the thing after N" has an sdk_int(), restrict this to
     // that platform version or higher.
@@ -125,9 +120,9 @@
     CHECK(sandbox.StartSandbox(
         sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED));
 
-    status_uma.set_status(RecordSeccompStatus::ENGAGED);
+    status_uma.set_status(SeccompSandboxStatus::ENGAGED);
   } else {
-    status_uma.set_status(RecordSeccompStatus::FEATURE_DISABLED);
+    status_uma.set_status(SeccompSandboxStatus::FEATURE_DISABLED);
   }
 #endif
   return true;
diff --git a/content/renderer/seccomp_sandbox_status_android.cc b/content/renderer/seccomp_sandbox_status_android.cc
new file mode 100644
index 0000000..34cb7bb92f
--- /dev/null
+++ b/content/renderer/seccomp_sandbox_status_android.cc
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/seccomp_sandbox_status_android.h"
+
+namespace content {
+
+static SeccompSandboxStatus g_status = SeccompSandboxStatus::NOT_SUPPORTED;
+
+void SetSeccompSandboxStatus(SeccompSandboxStatus status) {
+  g_status = status;
+}
+
+SeccompSandboxStatus GetSeccompSandboxStatus() {
+  return g_status;
+}
+
+}  // namespace content
diff --git a/content/renderer/seccomp_sandbox_status_android.h b/content/renderer/seccomp_sandbox_status_android.h
new file mode 100644
index 0000000..044f2c4
--- /dev/null
+++ b/content/renderer/seccomp_sandbox_status_android.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
+#define CONTENT_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
+
+#include "content/public/renderer/seccomp_sandbox_status_android.h"
+
+namespace content {
+
+void SetSeccompSandboxStatus(SeccompSandboxStatus status);
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_SECCOMP_SANDBOX_STATUS_ANDROID_H_
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index dfc5cea..0a43bbcc 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1683,10 +1683,6 @@
     ]
   }
 
-  if (enable_mojo_media) {
-    deps += [ "//media/mojo/services" ]
-  }
-
   # Screen capture unit tests.
   if (is_linux || is_mac || is_win) {
     deps += [ "//third_party/libyuv" ]
diff --git a/content/test/fuzzer/renderer_tree_fuzzer.cc b/content/test/fuzzer/renderer_tree_fuzzer.cc
index d941b55b..ecb6290 100644
--- a/content/test/fuzzer/renderer_tree_fuzzer.cc
+++ b/content/test/fuzzer/renderer_tree_fuzzer.cc
@@ -157,7 +157,7 @@
     }
 
     for (const auto& listItem : *list) {
-      std::unique_ptr<Node> node(Node::ParseJson(*listItem));
+      std::unique_ptr<Node> node(Node::ParseJson(listItem));
       if (node) {
         push_back(std::move(node));
       }
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 253c09df..559103a 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1474,23 +1474,48 @@
       'tab_capture_end2end_tests_run',
     ],
   },
-  'video_decode_accelerator_unittest': {
+  'video_decode_accelerator_d3d11_unittest': {
     'tester_configs': [
       {
         'os_types': ['win']
       },
     ],
-    'disabled_tester_configs': [
-      {
-        'names': [
-          'Linux ChromiumOS Ozone (Intel)',
-        ],
-      },
-    ],
     'args': [
+      '--use-angle=d3d11',
       '--use-test-data-path',
       '--test_video_data=test-25fps.h264:320:240:250:258:::1',
     ],
+    'test': 'video_decode_accelerator_unittest',
+  },
+  'video_decode_accelerator_d3d9_unittest': {
+    'tester_configs': [
+      {
+        # Run this on the FYI waterfall and optional tryservers.
+        'predicate': Predicates.FYI_ONLY,
+        'os_types': ['win']
+      },
+    ],
+    'args': [
+      '--use-angle=d3d9',
+      '--use-test-data-path',
+      '--test_video_data=test-25fps.h264:320:240:250:258:::1',
+    ],
+    'test': 'video_decode_accelerator_unittest',
+  },
+  'video_decode_accelerator_gl_unittest': {
+    'tester_configs': [
+      {
+        # Run this on the FYI waterfall and optional tryservers.
+        'predicate': Predicates.FYI_ONLY,
+        'os_types': ['win']
+      },
+    ],
+    'args': [
+      '--use-angle=gl',
+      '--use-test-data-path',
+      '--test_video_data=test-25fps.h264:320:240:250:258:::1',
+    ],
+    'test': 'video_decode_accelerator_unittest',
   },
 }
 
diff --git a/dbus/values_util.cc b/dbus/values_util.cc
index 1e035c9..e57380d 100644
--- a/dbus/values_util.cc
+++ b/dbus/values_util.cc
@@ -282,7 +282,7 @@
       dbus::MessageWriter array_writer(NULL);
       writer->OpenArray("v", &array_writer);
       for (const auto& value : *list) {
-        AppendValueDataAsVariant(&array_writer, *value);
+        AppendValueDataAsVariant(&array_writer, value);
       }
       writer->CloseContainer(&array_writer);
       break;
diff --git a/device/usb/mojo/device_impl.cc b/device/usb/mojo/device_impl.cc
index c68aa0fb..acbf468 100644
--- a/device/usb/mojo/device_impl.cc
+++ b/device/usb/mojo/device_impl.cc
@@ -99,28 +99,30 @@
 
 }  // namespace
 
-DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device,
-                       base::WeakPtr<PermissionProvider> permission_provider,
-                       DeviceRequest request)
-    : device_(device),
-      permission_provider_(permission_provider),
-      observer_(this),
-      binding_(this, std::move(request)),
-      weak_factory_(this) {
-  DCHECK(device_);
-  // This object owns itself and will be destroyed if,
-  //  * the device is disconnected or
-  //  * the message pipe it is bound to is closed or the message loop is
-  //  * destructed.
-  observer_.Add(device_.get());
-  binding_.set_connection_error_handler(
-      base::Bind([](DeviceImpl* self) { delete self; }, this));
+// static
+void DeviceImpl::Create(scoped_refptr<UsbDevice> device,
+                        base::WeakPtr<PermissionProvider> permission_provider,
+                        DeviceRequest request) {
+  auto* device_impl =
+      new DeviceImpl(std::move(device), std::move(permission_provider));
+  device_impl->binding_ = mojo::MakeStrongBinding(base::WrapUnique(device_impl),
+                                                  std::move(request));
 }
 
 DeviceImpl::~DeviceImpl() {
   CloseHandle();
 }
 
+DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device,
+                       base::WeakPtr<PermissionProvider> permission_provider)
+    : device_(std::move(device)),
+      permission_provider_(std::move(permission_provider)),
+      observer_(this),
+      weak_factory_(this) {
+  DCHECK(device_);
+  observer_.Add(device_.get());
+}
+
 void DeviceImpl::CloseHandle() {
   if (device_handle_) {
     device_handle_->Close();
@@ -434,7 +436,7 @@
 
 void DeviceImpl::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
   DCHECK_EQ(device_, device);
-  delete this;
+  binding_->Close();
 }
 
 }  // namespace usb
diff --git a/device/usb/mojo/device_impl.h b/device/usb/mojo/device_impl.h
index 9470e27..60a7756b 100644
--- a/device/usb/mojo/device_impl.h
+++ b/device/usb/mojo/device_impl.h
@@ -17,8 +17,7 @@
 #include "device/usb/public/interfaces/device.mojom.h"
 #include "device/usb/usb_device.h"
 #include "device/usb/usb_device_handle.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace device {
 namespace usb {
@@ -30,12 +29,16 @@
 // lifetime.
 class DeviceImpl : public Device, public device::UsbDevice::Observer {
  public:
-  DeviceImpl(scoped_refptr<UsbDevice> device,
-             base::WeakPtr<PermissionProvider> permission_provider,
-             DeviceRequest request);
+  static void Create(scoped_refptr<UsbDevice> device,
+                     base::WeakPtr<PermissionProvider> permission_provider,
+                     DeviceRequest request);
+
   ~DeviceImpl() override;
 
  private:
+  DeviceImpl(scoped_refptr<UsbDevice> device,
+             base::WeakPtr<PermissionProvider> permission_provider);
+
   // Closes the device if it's open. This will always set |device_handle_| to
   // null.
   void CloseHandle();
@@ -104,7 +107,7 @@
   // has been closed.
   scoped_refptr<UsbDeviceHandle> device_handle_;
 
-  mojo::Binding<Device> binding_;
+  mojo::StrongBindingPtr<Device> binding_;
   base::WeakPtrFactory<DeviceImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceImpl);
diff --git a/device/usb/mojo/device_impl_unittest.cc b/device/usb/mojo/device_impl_unittest.cc
index 8f48b21..8fbf075d6 100644
--- a/device/usb/mojo/device_impl_unittest.cc
+++ b/device/usb/mojo/device_impl_unittest.cc
@@ -161,11 +161,8 @@
     mock_handle_ = new MockUsbDeviceHandle(mock_device_.get());
 
     DevicePtr proxy;
-
-    // Owns itself.
-    new DeviceImpl(
-        mock_device_,
-        permission_provider_.GetWeakPtr(), mojo::MakeRequest(&proxy));
+    DeviceImpl::Create(mock_device_, permission_provider_.GetWeakPtr(),
+                       mojo::MakeRequest(&proxy));
 
     // Set up mock handle calls to respond based on mock device configs
     // established by the test.
diff --git a/device/usb/mojo/device_manager_impl.cc b/device/usb/mojo/device_manager_impl.cc
index 7e7b087..53a0834 100644
--- a/device/usb/mojo/device_manager_impl.cc
+++ b/device/usb/mojo/device_manager_impl.cc
@@ -21,8 +21,6 @@
 #include "device/usb/usb_device.h"
 #include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_service.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace device {
 namespace usb {
@@ -30,14 +28,16 @@
 // static
 void DeviceManagerImpl::Create(
     base::WeakPtr<PermissionProvider> permission_provider,
-    mojo::InterfaceRequest<DeviceManager> request) {
+    DeviceManagerRequest request) {
   DCHECK(DeviceClient::Get());
-  UsbService* usb_service = DeviceClient::Get()->GetUsbService();
-  if (usb_service) {
-    mojo::MakeStrongBinding(
-        base::MakeUnique<DeviceManagerImpl>(permission_provider, usb_service),
-        std::move(request));
-  }
+  UsbService* service = DeviceClient::Get()->GetUsbService();
+  if (!service)
+    return;
+
+  auto* device_manager_impl =
+      new DeviceManagerImpl(std::move(permission_provider), service);
+  device_manager_impl->binding_ = mojo::MakeStrongBinding(
+      base::WrapUnique(device_manager_impl), std::move(request));
 }
 
 DeviceManagerImpl::DeviceManagerImpl(
@@ -54,8 +54,6 @@
 }
 
 DeviceManagerImpl::~DeviceManagerImpl() {
-  if (!connection_error_handler_.is_null())
-    connection_error_handler_.Run();
 }
 
 void DeviceManagerImpl::GetDevices(EnumerationOptionsPtr options,
@@ -65,17 +63,16 @@
                                       base::Passed(&options), callback));
 }
 
-void DeviceManagerImpl::GetDevice(
-    const std::string& guid,
-    mojo::InterfaceRequest<Device> device_request) {
+void DeviceManagerImpl::GetDevice(const std::string& guid,
+                                  DeviceRequest device_request) {
   scoped_refptr<UsbDevice> device = usb_service_->GetDevice(guid);
   if (!device)
     return;
 
   if (permission_provider_ &&
       permission_provider_->HasDevicePermission(device)) {
-    // Owns itself.
-    new DeviceImpl(device, permission_provider_, std::move(device_request));
+    DeviceImpl::Create(std::move(device), permission_provider_,
+                       std::move(device_request));
   }
 }
 
@@ -117,7 +114,7 @@
 }
 
 void DeviceManagerImpl::WillDestroyUsbService() {
-  delete this;
+  binding_->Close();
 }
 
 }  // namespace usb
diff --git a/device/usb/mojo/device_manager_impl.h b/device/usb/mojo/device_manager_impl.h
index e962ace..9bb7549 100644
--- a/device/usb/mojo/device_manager_impl.h
+++ b/device/usb/mojo/device_manager_impl.h
@@ -16,7 +16,7 @@
 #include "base/scoped_observer.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_service.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace device {
 
@@ -31,22 +31,19 @@
 class DeviceManagerImpl : public DeviceManager, public UsbService::Observer {
  public:
   static void Create(base::WeakPtr<PermissionProvider> permission_provider,
-                     mojo::InterfaceRequest<DeviceManager> request);
+                     DeviceManagerRequest request);
 
-  DeviceManagerImpl(base::WeakPtr<PermissionProvider> permission_provider,
-                    UsbService* usb_service);
   ~DeviceManagerImpl() override;
 
-  void set_connection_error_handler(const base::Closure& error_handler) {
-    connection_error_handler_ = error_handler;
-  }
-
  private:
+  DeviceManagerImpl(base::WeakPtr<PermissionProvider> permission_provider,
+                    UsbService* usb_service);
+
   // DeviceManager implementation:
   void GetDevices(EnumerationOptionsPtr options,
                   const GetDevicesCallback& callback) override;
   void GetDevice(const std::string& guid,
-                 mojo::InterfaceRequest<Device> device_request) override;
+                 DeviceRequest device_request) override;
   void SetClient(DeviceManagerClientPtr client) override;
 
   // Callbacks to handle the async responses from the underlying UsbService.
@@ -61,14 +58,13 @@
 
   void MaybeRunDeviceChangesCallback();
 
+  mojo::StrongBindingPtr<DeviceManager> binding_;
   base::WeakPtr<PermissionProvider> permission_provider_;
 
   UsbService* usb_service_;
   ScopedObserver<UsbService, UsbService::Observer> observer_;
   DeviceManagerClientPtr client_;
 
-  base::Closure connection_error_handler_;
-
   base::WeakPtrFactory<DeviceManagerImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceManagerImpl);
diff --git a/extensions/browser/api/declarative/declarative_api.cc b/extensions/browser/api/declarative/declarative_api.cc
index 1b7ca18..b2393b2c 100644
--- a/extensions/browser/api/declarative/declarative_api.cc
+++ b/extensions/browser/api/declarative/declarative_api.cc
@@ -55,17 +55,17 @@
   size_t index = 0;
   for (base::ListValue::iterator iter = args->begin(); iter != args->end();
        ++iter, ++index) {
-    if ((*iter)->IsType(base::Value::Type::BINARY)) {
+    if (iter->IsType(base::Value::Type::BINARY)) {
       base::Value* binary = NULL;
       if (args->GetBinary(index, &binary))
         args->Set(index, ConvertBinaryToBase64(binary).release());
-    } else if ((*iter)->IsType(base::Value::Type::LIST)) {
+    } else if (iter->IsType(base::Value::Type::LIST)) {
       base::ListValue* list;
-      (*iter)->GetAsList(&list);
+      iter->GetAsList(&list);
       ConvertBinaryListElementsToBase64(list);
-    } else if ((*iter)->IsType(base::Value::Type::DICTIONARY)) {
+    } else if (iter->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* dict;
-      (*iter)->GetAsDictionary(&dict);
+      iter->GetAsDictionary(&dict);
       ConvertBinaryDictionaryValuesToBase64(dict);
     }
   }
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
index 53edf36..626c2433 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
@@ -235,7 +235,7 @@
   for (base::ListValue::const_iterator it = value_as_list->begin();
        it != value_as_list->end(); ++it) {
     std::string content_type;
-    if (!(*it)->GetAsString(&content_type)) {
+    if (!it->GetAsString(&content_type)) {
       *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
       return scoped_refptr<const WebRequestConditionAttribute>(NULL);
     }
@@ -384,7 +384,7 @@
   for (base::ListValue::const_iterator it = tests->begin();
        it != tests->end(); ++it) {
     const base::DictionaryValue* tests = NULL;
-    if (!(*it)->GetAsDictionary(&tests))
+    if (!it->GetAsDictionary(&tests))
       return std::unique_ptr<const HeaderMatcher>();
 
     std::unique_ptr<const HeaderMatchTest> header_test(
@@ -509,7 +509,7 @@
         const base::ListValue* list = NULL;
         CHECK(content->GetAsList(&list));
         for (const auto& it : *list) {
-          tests->push_back(StringMatchTest::Create(*it, match_type, !is_name));
+          tests->push_back(StringMatchTest::Create(it, match_type, !is_name));
         }
         break;
       }
@@ -810,7 +810,7 @@
   std::string stage_name;
   for (base::ListValue::const_iterator it = list->begin();
        it != list->end(); ++it) {
-    if (!((*it)->GetAsString(&stage_name)))
+    if (!(it->GetAsString(&stage_name)))
       return false;
     if (stage_name == keys::kOnBeforeRequestEnum) {
       stages |= ON_BEFORE_REQUEST;
diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc
index 427b56a..38b2c76 100644
--- a/extensions/browser/api/device_permissions_manager.cc
+++ b/extensions/browser/api/device_permissions_manager.cc
@@ -254,7 +254,7 @@
 
   for (const auto& entry : *devices) {
     const base::DictionaryValue* entry_dict;
-    if (entry->GetAsDictionary(&entry_dict)) {
+    if (entry.GetAsDictionary(&entry_dict)) {
       scoped_refptr<DevicePermissionEntry> device_entry =
           ReadDevicePermissionEntry(entry_dict);
       if (entry_dict) {
diff --git a/extensions/browser/api/networking_private/networking_private_api.cc b/extensions/browser/api/networking_private/networking_private_api.cc
index 61e7ff1..d887a39 100644
--- a/extensions/browser/api/networking_private/networking_private_api.cc
+++ b/extensions/browser/api/networking_private/networking_private_api.cc
@@ -476,7 +476,7 @@
   for (base::ListValue::iterator iter = enabled_networks_onc_types->begin();
        iter != enabled_networks_onc_types->end(); ++iter) {
     std::string type;
-    if (!(*iter)->GetAsString(&type))
+    if (!iter->GetAsString(&type))
       NOTREACHED();
     if (type == ::onc::network_type::kEthernet) {
       enabled_networks_list->AppendString(
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 380e7877..58d4494 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -512,9 +512,9 @@
       chromeos::network_util::TranslateNetworkListToONC(
           pattern, configured_only, visible_only, limit);
 
-  for (const auto& value : *network_properties_list) {
+  for (auto& value : *network_properties_list) {
     base::DictionaryValue* network_dict = nullptr;
-    value->GetAsDictionary(&network_dict);
+    value.GetAsDictionary(&network_dict);
     DCHECK(network_dict);
     if (GetThirdPartyVPNDictionary(network_dict))
       AppendThirdPartyProviderName(network_dict);
diff --git a/extensions/browser/api/networking_private/networking_private_linux.cc b/extensions/browser/api/networking_private/networking_private_linux.cc
index a184c55f..d7ecf8e 100644
--- a/extensions/browser/api/networking_private/networking_private_linux.cc
+++ b/extensions/browser/api/networking_private/networking_private_linux.cc
@@ -643,8 +643,8 @@
 
   for (const auto& network : network_list) {
     std::string guid;
-    base::DictionaryValue* dict;
-    if (network->GetAsDictionary(&dict)) {
+    const base::DictionaryValue* dict = nullptr;
+    if (network.GetAsDictionary(&dict)) {
       if (dict->GetString(kAccessPointInfoGuid, &guid)) {
         guidsForEventCallback.push_back(guid);
       }
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
index e6403a3..a844b4b 100644
--- a/extensions/browser/api/storage/storage_api.cc
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -108,7 +108,7 @@
   std::string as_string;
   for (base::ListValue::const_iterator it = from.begin();
        it != from.end(); ++it) {
-    if ((*it)->GetAsString(&as_string)) {
+    if (it->GetAsString(&as_string)) {
       to->push_back(as_string);
     }
   }
diff --git a/extensions/browser/api/system_network/system_network_api_unittest.cc b/extensions/browser/api/system_network/system_network_api_unittest.cc
index 1fde445..c2fc879 100644
--- a/extensions/browser/api/system_network/system_network_api_unittest.cc
+++ b/extensions/browser/api/system_network/system_network_api_unittest.cc
@@ -39,7 +39,7 @@
 
   for (const auto& network_interface_value : *value) {
     NetworkInterface network_interface;
-    ASSERT_TRUE(NetworkInterface::Populate(*network_interface_value,
+    ASSERT_TRUE(NetworkInterface::Populate(network_interface_value,
                                            &network_interface));
 
     LOG(INFO) << "Network interface: address=" << network_interface.address
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 01ca8d3c..8324617 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1803,7 +1803,7 @@
   std::string extension_id;
   for (base::ListValue::const_iterator value_it = user_pref_as_list->begin();
        value_it != user_pref_as_list->end(); ++value_it) {
-    if (!(*value_it)->GetAsString(&extension_id)) {
+    if (!value_it->GetAsString(&extension_id)) {
       NOTREACHED();
       continue;
     }
diff --git a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
index c013665..2ee74f7 100644
--- a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -80,6 +81,14 @@
         base::StringPrintf("runTest('%s');", test_name.c_str())));
     ASSERT_TRUE(test_run_listener.WaitUntilSatisfied());
   }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WebViewAPITest::SetUpCommandLine(command_line);
+    // This switch ensures that there will always be at least one media device,
+    // even on machines without physical devices. This is required by tests that
+    // request permission to use media devices.
+    command_line->AppendSwitch("use-fake-device-for-media-stream");
+  }
 };
 
 IN_PROC_BROWSER_TEST_F(WebViewMediaAccessAPITest, TestAllow) {
diff --git a/extensions/browser/verified_contents.cc b/extensions/browser/verified_contents.cc
index fd60f313..eaf4c8f 100644
--- a/extensions/browser/verified_contents.cc
+++ b/extensions/browser/verified_contents.cc
@@ -43,12 +43,12 @@
 
 // Helper function to iterate over a list of dictionaries, returning the
 // dictionary that has |key| -> |value| in it, if any, or NULL.
-DictionaryValue* FindDictionaryWithValue(const ListValue* list,
-                                         const std::string& key,
-                                         const std::string& value) {
+const DictionaryValue* FindDictionaryWithValue(const ListValue* list,
+                                               const std::string& key,
+                                               const std::string& value) {
   for (const auto& i : *list) {
-    DictionaryValue* dictionary;
-    if (!i->GetAsDictionary(&dictionary))
+    const DictionaryValue* dictionary;
+    if (!i.GetAsDictionary(&dictionary))
       continue;
     std::string found_value;
     if (dictionary->GetString(key, &found_value) && found_value == value)
@@ -248,20 +248,20 @@
   //     }
   //   }
   // ]
-  DictionaryValue* dictionary =
+  const DictionaryValue* dictionary =
       FindDictionaryWithValue(top_list, kDescriptionKey, kTreeHashPerFile);
-  DictionaryValue* signed_content = NULL;
+  const DictionaryValue* signed_content = NULL;
   if (!dictionary ||
       !dictionary->GetDictionaryWithoutPathExpansion(kSignedContentKey,
                                                      &signed_content)) {
     return false;
   }
 
-  ListValue* signatures = NULL;
+  const ListValue* signatures = NULL;
   if (!signed_content->GetList(kSignaturesKey, &signatures))
     return false;
 
-  DictionaryValue* signature_dict =
+  const DictionaryValue* signature_dict =
       FindDictionaryWithValue(signatures, kHeaderKidKey, kWebstoreKId);
   if (!signature_dict)
     return false;
diff --git a/extensions/common/api/declarative/declarative_manifest_data.cc b/extensions/common/api/declarative/declarative_manifest_data.cc
index 13bfdcc5..31011309 100644
--- a/extensions/common/api/declarative/declarative_manifest_data.cc
+++ b/extensions/common/api/declarative/declarative_manifest_data.cc
@@ -129,9 +129,9 @@
 
   for (const auto& element : *list) {
     const base::DictionaryValue* dict = nullptr;
-    if (!element->GetAsDictionary(&dict)) {
+    if (!element.GetAsDictionary(&dict)) {
       error_builder.Append("expected dictionary, got %s",
-                           base::Value::GetTypeName(element->GetType()));
+                           base::Value::GetTypeName(element.GetType()));
       return std::unique_ptr<DeclarativeManifestData>();
     }
     std::string event;
diff --git a/extensions/common/extension_l10n_util_unittest.cc b/extensions/common/extension_l10n_util_unittest.cc
index 3caba36..81d7d78f 100644
--- a/extensions/common/extension_l10n_util_unittest.cc
+++ b/extensions/common/extension_l10n_util_unittest.cc
@@ -428,8 +428,9 @@
   manifest.SetString(keys::kDescription, "__MSG_description__");
   base::ListValue* handlers = new base::ListValue();
   manifest.Set(keys::kFileBrowserHandlers, handlers);
-  base::DictionaryValue* handler = new base::DictionaryValue();
-  handlers->Append(base::WrapUnique(handler));
+  handlers->Append(base::MakeUnique<base::DictionaryValue>());
+  base::DictionaryValue* handler = nullptr;
+  handlers->GetDictionary(0, &handler);
   handler->SetString(keys::kPageActionDefaultTitle,
                      "__MSG_file_handler_title__");
 
diff --git a/extensions/common/manifest_handlers/action_handlers_handler.cc b/extensions/common/manifest_handlers/action_handlers_handler.cc
index 2d30318..c5ac38d 100644
--- a/extensions/common/manifest_handlers/action_handlers_handler.cc
+++ b/extensions/common/manifest_handlers/action_handlers_handler.cc
@@ -42,9 +42,9 @@
   }
 
   auto info = base::MakeUnique<ActionHandlersInfo>();
-  for (const std::unique_ptr<base::Value>& wrapped_value : *entries) {
+  for (const base::Value& wrapped_value : *entries) {
     std::string value;
-    if (!wrapped_value->GetAsString(&value)) {
+    if (!wrapped_value.GetAsString(&value)) {
       *error = base::ASCIIToUTF16(errors::kInvalidActionHandlersType);
       return false;
     }
diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.cc b/extensions/common/manifest_handlers/kiosk_mode_info.cc
index b9947e28c..13bb071 100644
--- a/extensions/common/manifest_handlers/kiosk_mode_info.cc
+++ b/extensions/common/manifest_handlers/kiosk_mode_info.cc
@@ -119,7 +119,7 @@
 
     for (const auto& value : *list) {
       std::unique_ptr<KioskSecondaryAppsType> app =
-          KioskSecondaryAppsType::FromValue(*value, error);
+          KioskSecondaryAppsType::FromValue(value, error);
       if (!app) {
         *error = base::ASCIIToUTF16(
             manifest_errors::kInvalidKioskSecondaryAppsBadAppId);
diff --git a/extensions/common/manifest_handlers/requirements_info.cc b/extensions/common/manifest_handlers/requirements_info.cc
index d73af89..0acbf7b 100644
--- a/extensions/common/manifest_handlers/requirements_info.cc
+++ b/extensions/common/manifest_handlers/requirements_info.cc
@@ -117,7 +117,7 @@
       for (base::ListValue::const_iterator feature_iter = features->begin();
            feature_iter != features->end(); ++feature_iter) {
         std::string feature;
-        if ((*feature_iter)->GetAsString(&feature)) {
+        if (feature_iter->GetAsString(&feature)) {
           if (feature == "webgl") {
             requirements->webgl = true;
           } else if (feature == "css3d") {
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc
index 6c3cc2e..8b36c2d 100644
--- a/extensions/renderer/api_binding.cc
+++ b/extensions/renderer/api_binding.cc
@@ -180,7 +180,7 @@
   if (function_definitions) {
     for (const auto& func : *function_definitions) {
       const base::DictionaryValue* func_dict = nullptr;
-      CHECK(func->GetAsDictionary(&func_dict));
+      CHECK(func.GetAsDictionary(&func_dict));
       std::string name;
       CHECK(func_dict->GetString("name", &name));
 
@@ -197,7 +197,7 @@
   if (type_definitions) {
     for (const auto& type : *type_definitions) {
       const base::DictionaryValue* type_dict = nullptr;
-      CHECK(type->GetAsDictionary(&type_dict));
+      CHECK(type.GetAsDictionary(&type_dict));
       std::string id;
       CHECK(type_dict->GetString("id", &id));
       auto argument_spec = base::MakeUnique<ArgumentSpec>(*type_dict);
@@ -222,7 +222,7 @@
       if (type_dict->GetList("functions", &type_functions)) {
         for (const auto& func : *type_functions) {
           const base::DictionaryValue* func_dict = nullptr;
-          CHECK(func->GetAsDictionary(&func_dict));
+          CHECK(func.GetAsDictionary(&func_dict));
           std::string function_name;
           CHECK(func_dict->GetString("name", &function_name));
 
@@ -240,7 +240,7 @@
     events_.reserve(event_definitions->GetSize());
     for (const auto& event : *event_definitions) {
       const base::DictionaryValue* event_dict = nullptr;
-      CHECK(event->GetAsDictionary(&event_dict));
+      CHECK(event.GetAsDictionary(&event_dict));
       std::string name;
       CHECK(event_dict->GetString("name", &name));
       std::string full_name =
@@ -265,8 +265,8 @@
           const base::ListValue* list = nullptr;
           CHECK(options->GetList(name, &list));
           for (const auto& entry : *list) {
-            DCHECK(entry->is_string());
-            out_value->push_back(entry->GetString());
+            DCHECK(entry.is_string());
+            out_value->push_back(entry.GetString());
           }
         };
         get_values("actions", &rule_actions);
diff --git a/extensions/renderer/api_event_handler.cc b/extensions/renderer/api_event_handler.cc
index fc2d47a..acd468c 100644
--- a/extensions/renderer/api_event_handler.cc
+++ b/extensions/renderer/api_event_handler.cc
@@ -220,7 +220,7 @@
     std::vector<v8::Local<v8::Value>> v8_args;
     v8_args.reserve(args.GetSize());
     for (const auto& arg : args)
-      v8_args.push_back(converter->ToV8Value(arg.get(), context));
+      v8_args.push_back(converter->ToV8Value(&arg, context));
     emitter->Fire(context, &v8_args, &filter);
   } else {
     v8::Isolate* isolate = context->GetIsolate();
diff --git a/extensions/renderer/api_request_handler.cc b/extensions/renderer/api_request_handler.cc
index 1771dce..369cfcb1 100644
--- a/extensions/renderer/api_request_handler.cc
+++ b/extensions/renderer/api_request_handler.cc
@@ -127,7 +127,7 @@
   for (const auto& arg : pending_request.callback_arguments)
     args.push_back(arg.Get(isolate));
   for (const auto& arg : response_args)
-    args.push_back(converter->ToV8Value(arg.get(), context));
+    args.push_back(converter->ToV8Value(&arg, context));
 
   blink::WebScopedUserGesture user_gesture(pending_request.user_gesture_token);
   if (!error.empty())
diff --git a/extensions/renderer/api_signature.cc b/extensions/renderer/api_signature.cc
index 683ba1ff..c83eb91 100644
--- a/extensions/renderer/api_signature.cc
+++ b/extensions/renderer/api_signature.cc
@@ -234,7 +234,7 @@
   signature_.reserve(specification.GetSize());
   for (const auto& value : specification) {
     const base::DictionaryValue* param = nullptr;
-    CHECK(value->GetAsDictionary(&param));
+    CHECK(value.GetAsDictionary(&param));
     signature_.push_back(base::MakeUnique<ArgumentSpec>(*param));
   }
 }
diff --git a/extensions/renderer/argument_spec.cc b/extensions/renderer/argument_spec.cc
index b0cb7c3..1836714 100644
--- a/extensions/renderer/argument_spec.cc
+++ b/extensions/renderer/argument_spec.cc
@@ -60,7 +60,7 @@
       type_ = ArgumentType::CHOICES;
       choices_.reserve(choices->GetSize());
       for (const auto& choice : *choices)
-        choices_.push_back(base::MakeUnique<ArgumentSpec>(*choice));
+        choices_.push_back(base::MakeUnique<ArgumentSpec>(choice));
       return;
     }
   }
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index fcc528de8..28e2b0b 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -154,7 +154,7 @@
 
   std::vector<v8::Local<v8::Value>> arguments;
   for (const auto& arg : *args) {
-    arguments.push_back(converter->ToV8Value(arg.get(), context->v8_context()));
+    arguments.push_back(converter->ToV8Value(&arg, context->v8_context()));
   }
 
   context->module_system()->CallModuleMethodSafe(
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc
index 03bd8a45..c5c3f1c 100644
--- a/google_apis/gaia/gaia_auth_fetcher.cc
+++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -499,7 +499,7 @@
        iter != sessionsList->end();
        iter++) {
     base::DictionaryValue* sessionDictionary;
-    if (!(*iter)->GetAsDictionary(&sessionDictionary))
+    if (!iter->GetAsDictionary(&sessionDictionary))
       continue;
 
     if (sessionDictionary->GetString("login_hint", login_hint))
diff --git a/headless/public/internal/value_conversions.h b/headless/public/internal/value_conversions.h
index 5622fb84..f2ded264 100644
--- a/headless/public/internal/value_conversions.h
+++ b/headless/public/internal/value_conversions.h
@@ -151,7 +151,7 @@
     }
     errors->Push();
     for (const auto& item : *list)
-      result.push_back(FromValue<T>::Parse(*item, errors));
+      result.push_back(FromValue<T>::Parse(item, errors));
     errors->Pop();
     return result;
   }
diff --git a/ios/chrome/browser/autofill/autofill_agent.mm b/ios/chrome/browser/autofill/autofill_agent.mm
index db8e6ab1..651542f4 100644
--- a/ios/chrome/browser/autofill/autofill_agent.mm
+++ b/ios/chrome/browser/autofill/autofill_agent.mm
@@ -267,7 +267,7 @@
   if (field.GetList("option_values", &optionValues)) {
     for (const auto& optionValue : *optionValues) {
       base::string16 value;
-      if (optionValue->GetAsString(&value))
+      if (optionValue.GetAsString(&value))
         fieldData->option_values.push_back(value);
     }
   }
@@ -277,7 +277,7 @@
   if (field.GetList("option_contents", &optionContents)) {
     for (const auto& optionContent : *optionContents) {
       base::string16 content;
-      if (optionContent->GetAsString(&content))
+      if (optionContent.GetAsString(&content))
         fieldData->option_contents.push_back(content);
     }
   }
@@ -365,7 +365,7 @@
   for (const auto& formDict : *formsList) {
     // Each form list entry should be a JSON dictionary.
     const base::DictionaryValue* formData;
-    if (!formDict->GetAsDictionary(&formData))
+    if (!formDict.GetAsDictionary(&formData))
       return NO;
 
     // Form data is copied into a FormData object field-by-field.
@@ -404,7 +404,7 @@
     for (const auto& fieldDict : *fieldsList) {
       const base::DictionaryValue* field;
       autofill::FormFieldData fieldData;
-      if (fieldDict->GetAsDictionary(&field) &&
+      if (fieldDict.GetAsDictionary(&field) &&
           [self extractFormField:*field asFieldData:&fieldData]) {
         form.fields.push_back(fieldData);
       } else {
diff --git a/ios/chrome/browser/ui/bookmarks/bars/BUILD.gn b/ios/chrome/browser/ui/bookmarks/bars/BUILD.gn
index 47c8cfe..6174d44a 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/bars/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("bars") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "bookmark_editing_bar.h",
     "bookmark_editing_bar.mm",
diff --git a/ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.mm b/ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.mm
index 3173edd..73412092 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.mm
+++ b/ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.mm
@@ -5,8 +5,7 @@
 #import "ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.h"
 
 #include "base/logging.h"
-#include "base/mac/objc_property_releaser.h"
-#include "base/mac/scoped_nsobject.h"
+
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_extended_button.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
@@ -15,22 +14,25 @@
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 // The distance between buttons.
 CGFloat kInterButtonMargin = 24;
 }  // namespace
 
 @interface BookmarkEditingBar () {
-  base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkEditingBar;
 }
-@property(nonatomic, retain) BookmarkExtendedButton* cancelButton;
+@property(nonatomic, strong) BookmarkExtendedButton* cancelButton;
 // This label is slightly left off center, and reflects the number of bookmarks
 // selected for editing.
-@property(nonatomic, retain) UILabel* countLabel;
-@property(nonatomic, retain) BookmarkExtendedButton* deleteButton;
-@property(nonatomic, retain) UIView* dropShadow;
-@property(nonatomic, retain) BookmarkExtendedButton* editButton;
-@property(nonatomic, retain) BookmarkExtendedButton* moveButton;
+@property(nonatomic, strong) UILabel* countLabel;
+@property(nonatomic, strong) BookmarkExtendedButton* deleteButton;
+@property(nonatomic, strong) UIView* dropShadow;
+@property(nonatomic, strong) BookmarkExtendedButton* editButton;
+@property(nonatomic, strong) BookmarkExtendedButton* moveButton;
 @end
 
 @implementation BookmarkEditingBar
@@ -44,7 +46,6 @@
 - (id)initWithFrame:(CGRect)outerFrame {
   self = [super initWithFrame:outerFrame];
   if (self) {
-    _propertyReleaser_BookmarkEditingBar.Init(self, [BookmarkEditingBar class]);
     self.backgroundColor = bookmark_utils_ios::blueColor();
 
     CGRect bounds = self.contentView.bounds;
@@ -55,13 +56,12 @@
     CGFloat cancelButtonY =
         floor((bounds.size.height - cancelButtonHeight) / 2);
     CGFloat cancelButtonX = cancelButtonY;
-    base::scoped_nsobject<BookmarkExtendedButton> button(
-        [[BookmarkExtendedButton alloc]
-            initWithFrame:LayoutRectGetRect(LayoutRectMake(
-                              cancelButtonX,
-                              CGRectGetWidth(self.contentView.bounds),
-                              cancelButtonY, cancelButtonWidth,
-                              cancelButtonHeight))]);
+    BookmarkExtendedButton* button = [[BookmarkExtendedButton alloc]
+        initWithFrame:LayoutRectGetRect(LayoutRectMake(
+                          cancelButtonX,
+                          CGRectGetWidth(self.contentView.bounds),
+                          cancelButtonY, cancelButtonWidth,
+                          cancelButtonHeight))];
     self.cancelButton = button;
     self.cancelButton.extendedEdges = UIEdgeInsetsMakeDirected(
         cancelButtonY, cancelButtonX, cancelButtonY, cancelButtonX);
@@ -79,10 +79,10 @@
     // Add the count label to the right of the cancel button.
     CGFloat labelX = bookmark_utils_ios::titleMargin;
     CGFloat labelY = 0;
-    base::scoped_nsobject<UILabel> label([[UILabel alloc]
+    UILabel* label = [[UILabel alloc]
         initWithFrame:LayoutRectGetRect(LayoutRectMake(
                           labelX, CGRectGetWidth(self.contentView.bounds),
-                          labelY, 150, CGRectGetHeight(bounds)))]);
+                          labelY, 150, CGRectGetHeight(bounds)))];
     self.countLabel = label;
     self.countLabel.textColor = [UIColor whiteColor];
     self.countLabel.autoresizingMask =
@@ -99,10 +99,10 @@
     CGFloat editButtonRightMargin = editButtonY;
     CGFloat editButtonX =
         bounds.size.width - editButtonRightMargin - editButtonWidth;
-    button.reset([[BookmarkExtendedButton alloc]
+    button = [[BookmarkExtendedButton alloc]
         initWithFrame:LayoutRectGetRect(LayoutRectMake(
                           editButtonX, CGRectGetWidth(self.contentView.bounds),
-                          editButtonY, editButtonWidth, editButtonHeight))]);
+                          editButtonY, editButtonWidth, editButtonHeight))];
     self.editButton = button;
     self.editButton.extendedEdges =
         UIEdgeInsetsMakeDirected(editButtonY, kInterButtonMargin / 2.0,
@@ -119,8 +119,8 @@
     [self.contentView addSubview:self.editButton];
 
     // Add the move button to the same position as the edit button.
-    button.reset(
-        [[BookmarkExtendedButton alloc] initWithFrame:self.editButton.frame]);
+    button =
+        [[BookmarkExtendedButton alloc] initWithFrame:self.editButton.frame];
     self.moveButton = button;
     self.moveButton.extendedEdges =
         UIEdgeInsetsMakeDirected(editButtonY, kInterButtonMargin / 2.0,
@@ -145,12 +145,12 @@
         CGRectGetLeadingLayoutOffsetInBoundingRect(self.editButton.frame,
                                                    self.contentView.bounds) -
         kInterButtonMargin - deleteButtonWidth;
-    button.reset([[BookmarkExtendedButton alloc]
+    button = [[BookmarkExtendedButton alloc]
         initWithFrame:LayoutRectGetRect(LayoutRectMake(
                           deleteButtonX,
                           CGRectGetWidth(self.contentView.bounds),
                           deleteButtonY, deleteButtonWidth,
-                          deleteButtonHeight))]);
+                          deleteButtonHeight))];
     self.deleteButton = button;
     self.deleteButton.extendedEdges = UIEdgeInsetsMakeDirected(
         deleteButtonY, deleteButtonY, deleteButtonY, kInterButtonMargin / 2.0);
diff --git a/ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.mm b/ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.mm
index 2c3b19f..a4810f3 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.mm
+++ b/ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.mm
@@ -5,9 +5,6 @@
 #import "ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.h"
 
 #import <QuartzCore/QuartzCore.h>
-
-#include "base/mac/objc_property_releaser.h"
-#include "base/mac/scoped_nsobject.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_extended_button.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
 #import "ios/chrome/browser/ui/rtl_geometry.h"
@@ -16,6 +13,10 @@
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 const CGFloat kButtonHeight = 24;
 const CGFloat kButtonWidth = 24;
@@ -27,17 +28,16 @@
 const CGFloat kInterButtonMargin = 24;
 };  // namespace
 
-@interface BookmarkNavigationBar () {
-  base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkNavigationBar;
-}
-@property(nonatomic, retain) BookmarkExtendedButton* cancelButton;
+@interface BookmarkNavigationBar ()
+
+@property(nonatomic, strong) BookmarkExtendedButton* cancelButton;
 // All subviews are added to |contentView|, which allows easy repositioning of
 // the content to account for iOS 6 and iOS 7+ layout differences.
-@property(nonatomic, retain) UIView* contentView;
-@property(nonatomic, retain) BookmarkExtendedButton* backButton;
-@property(nonatomic, retain) BookmarkExtendedButton* editButton;
-@property(nonatomic, retain) BookmarkExtendedButton* menuButton;
-@property(nonatomic, retain) UILabel* titleLabel;
+@property(nonatomic, strong) UIView* contentView;
+@property(nonatomic, strong) BookmarkExtendedButton* backButton;
+@property(nonatomic, strong) BookmarkExtendedButton* editButton;
+@property(nonatomic, strong) BookmarkExtendedButton* menuButton;
+@property(nonatomic, strong) UILabel* titleLabel;
 @end
 
 @implementation BookmarkNavigationBar
@@ -51,25 +51,22 @@
 - (id)initWithFrame:(CGRect)outerFrame {
   self = [super initWithFrame:outerFrame];
   if (self) {
-    _propertyReleaser_BookmarkNavigationBar.Init(self,
-                                                 [BookmarkNavigationBar class]);
-
     self.backgroundColor = bookmark_utils_ios::mainBackgroundColor();
     self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin |
                             UIViewAutoresizingFlexibleWidth;
 
     // Position the content view at the bottom of |self|.
     CGFloat contentY = CGRectGetHeight(outerFrame) - kContentHeight;
-    self.contentView = base::scoped_nsobject<UIView>([[UIView alloc]
+    self.contentView = [[UIView alloc]
         initWithFrame:CGRectMake(0, contentY, CGRectGetWidth(outerFrame),
-                                 kContentHeight)]);
+                                 kContentHeight)];
     [self addSubview:self.contentView];
     self.contentView.backgroundColor = [UIColor clearColor];
     self.contentView.autoresizingMask =
         UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
     CGRect contentViewBounds = self.contentView.bounds;
 
-    base::scoped_nsobject<UILabel> label([[UILabel alloc] init]);
+    UILabel* label = [[UILabel alloc] init];
     self.titleLabel = label;
     self.titleLabel.textColor = [UIColor colorWithWhite:68 / 255.0 alpha:1.0];
     self.titleLabel.backgroundColor = [UIColor clearColor];
@@ -86,8 +83,8 @@
     CGFloat buttonX = CGRectGetWidth(contentViewBounds) - buttonSideMargin;
 
     if (!IsIPadIdiom()) {
-      base::scoped_nsobject<BookmarkExtendedButton> button(
-          [[BookmarkExtendedButton alloc] initWithFrame:CGRectZero]);
+      BookmarkExtendedButton* button =
+          [[BookmarkExtendedButton alloc] initWithFrame:CGRectZero];
 
       self.cancelButton = button;
       self.cancelButton.autoresizingMask =
@@ -128,8 +125,8 @@
         UIEdgeInsetsMake(buttonVerticalMargin, kInterButtonMargin / 2.0,
                          buttonVerticalMargin, kInterButtonMargin / 2.0);
 
-    base::scoped_nsobject<BookmarkExtendedButton> editButton(
-        [[BookmarkExtendedButton alloc] initWithFrame:buttonFrame]);
+    BookmarkExtendedButton* editButton =
+        [[BookmarkExtendedButton alloc] initWithFrame:buttonFrame];
     self.editButton = editButton;
     self.editButton.extendedEdges = buttonInsets;
     self.editButton.autoresizingMask =
@@ -150,8 +147,8 @@
         UIEdgeInsetsMake(buttonVerticalMargin, buttonSideMargin,
                          buttonVerticalMargin, buttonSideMargin);
 
-    base::scoped_nsobject<BookmarkExtendedButton> menuButton(
-        [[BookmarkExtendedButton alloc] initWithFrame:leftButtonFrame]);
+    BookmarkExtendedButton* menuButton =
+        [[BookmarkExtendedButton alloc] initWithFrame:leftButtonFrame];
     self.menuButton = menuButton;
     self.menuButton.extendedEdges = leftButtonInsets;
     self.menuButton.autoresizingMask =
@@ -165,8 +162,8 @@
     self.menuButton.accessibilityIdentifier = @"Menu";
     [self.contentView addSubview:self.menuButton];
     self.menuButton.hidden = YES;
-    base::scoped_nsobject<BookmarkExtendedButton> backButton(
-        [[BookmarkExtendedButton alloc] initWithFrame:leftButtonFrame]);
+    BookmarkExtendedButton* backButton =
+        [[BookmarkExtendedButton alloc] initWithFrame:leftButtonFrame];
     self.backButton = backButton;
     self.backButton.extendedEdges = leftButtonInsets;
     self.backButton.autoresizingMask =
diff --git a/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.h b/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.h
index 051121c..d3c2227 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.h
+++ b/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.h
@@ -11,7 +11,7 @@
 // All subviews should be added to |contentView|, which allows easy
 // repositioning of the content to account for iOS 6 and iOS 7+ layout
 // differences.
-@property(nonatomic, readonly) UIView* contentView;
+@property(strong, nonatomic, readonly) UIView* contentView;
 
 // The height that this view's content view will be instantiated at. This is
 // independent of the status bar.
diff --git a/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.mm b/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.mm
index 3aaa4ad..7102d540 100644
--- a/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.mm
+++ b/ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.mm
@@ -3,14 +3,9 @@
 // found in the LICENSE file.
 #import "ios/chrome/browser/ui/bookmarks/bars/bookmark_top_bar.h"
 
-#include "base/mac/objc_property_releaser.h"
-#include "base/mac/scoped_nsobject.h"
-
-@interface BookmarkTopBar () {
-  base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkBar;
-}
-@property(nonatomic, retain) UIView* contentView;
-@end
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 @implementation BookmarkTopBar
 
@@ -23,15 +18,11 @@
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
-    _propertyReleaser_BookmarkBar.Init(self, [BookmarkTopBar class]);
-
     self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin |
                             UIViewAutoresizingFlexibleWidth;
 
-    base::scoped_nsobject<UIView> contentView([[UIView alloc] init]);
-    self.contentView.backgroundColor = [UIColor clearColor];
-    [self addSubview:contentView];
-    self.contentView = contentView;
+    _contentView = [[UIView alloc] init];
+    [self addSubview:_contentView];
 
     [self statelessLayoutContentView];
   }
diff --git a/ios/chrome/browser/ui/qr_scanner/BUILD.gn b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
index 5033e50..1eb05b7 100644
--- a/ios/chrome/browser/ui/qr_scanner/BUILD.gn
+++ b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
@@ -20,6 +20,7 @@
 }
 
 source_set("qr_scanner") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "camera_controller.h",
     "camera_controller.mm",
diff --git a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
index d6c94179..b4fded5 100644
--- a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
+++ b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
@@ -4,21 +4,23 @@
 
 #import "ios/chrome/browser/ui/qr_scanner/camera_controller.h"
 
-#include "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/strings/stringprintf.h"
 #include "ios/chrome/common/ios_app_bundle_id_prefix.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface CameraController ()<AVCaptureMetadataOutputObjectsDelegate> {
   // The capture session for recording video and detecting QR codes.
-  base::scoped_nsobject<AVCaptureSession> _captureSession;
+  AVCaptureSession* _captureSession;
   // The metadata output attached to the capture session.
-  base::scoped_nsobject<AVCaptureMetadataOutput> _metadataOutput;
+  AVCaptureMetadataOutput* _metadataOutput;
   // The delegate which receives the scanned result. All methods of this
   // delegate should be called on the main queue.
-  base::WeakNSProtocol<id<CameraControllerDelegate>> _delegate;
+  __weak id<CameraControllerDelegate> _delegate;
   // The queue for dispatching calls to |_captureSession|.
   dispatch_queue_t _sessionQueue;
 }
@@ -61,7 +63,7 @@
   if (self) {
     DCHECK(delegate);
     _cameraState = qr_scanner::CAMERA_NOT_LOADED;
-    _delegate.reset(delegate);
+    _delegate = delegate;
     std::string queueName =
         base::StringPrintf("%s.chrome.ios.QRScannerCaptureSessionQueue",
                            BUILDFLAG(IOS_APP_BUNDLE_ID_PREFIX));
@@ -76,8 +78,6 @@
 
 - (void)dealloc {
   [self stopReceivingNotifications];
-  dispatch_release(_sessionQueue);
-  [super dealloc];
 }
 
 #pragma mark public methods
@@ -199,7 +199,7 @@
       return;
     }
 
-    AVCaptureSession* session = [[[AVCaptureSession alloc] init] autorelease];
+    AVCaptureSession* session = [[AVCaptureSession alloc] init];
     if (![session canAddInput:videoInput]) {
       [self setCameraState:qr_scanner::CAMERA_UNAVAILABLE];
       return;
@@ -208,7 +208,7 @@
 
     // Configure metadata output.
     AVCaptureMetadataOutput* metadataOutput =
-        [[[AVCaptureMetadataOutput alloc] init] autorelease];
+        [[AVCaptureMetadataOutput alloc] init];
     [metadataOutput setMetadataObjectsDelegate:self
                                          queue:dispatch_get_main_queue()];
     if (![session canAddOutput:metadataOutput]) {
@@ -224,9 +224,9 @@
       return;
     }
     [metadataOutput setMetadataObjectTypes:availableCodeTypes];
-    _metadataOutput.reset([metadataOutput retain]);
+    _metadataOutput = metadataOutput;
 
-    _captureSession.reset([session retain]);
+    _captureSession = session;
     [self setCameraState:qr_scanner::CAMERA_AVAILABLE];
     // Setup torchAvailable.
     [self
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_alerts.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_alerts.mm
index 879cd1d..fa6fc93e 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_alerts.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_alerts.mm
@@ -14,6 +14,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // Returns a "Cancel" UIAlertAction for the given |block|.
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_transitioning_delegate.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_transitioning_delegate.mm
index a4848901..89967d3 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_transitioning_delegate.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_transitioning_delegate.mm
@@ -6,6 +6,10 @@
 
 #include "base/ios/block_types.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // The default animation duration.
@@ -58,7 +62,7 @@
   ProceduralBlock animations;
 
   switch (_transition) {
-    case PRESENT:
+    case PRESENT: {
       [containerView insertSubview:presentedView belowSubview:presentingView];
       initialFrame = finalFrame;
       finalFrame.origin.y = -finalFrame.size.height;
@@ -66,7 +70,8 @@
         [presentingView setFrame:finalFrame];
       };
       break;
-    case DISMISS:
+    }
+    case DISMISS: {
       [containerView insertSubview:presentedView aboveSubview:presentingView];
       initialFrame = finalFrame;
       initialFrame.origin.y = -initialFrame.size.height;
@@ -74,6 +79,7 @@
         [presentedView setFrame:finalFrame];
       };
       break;
+    }
   }
 
   // Set the frame for the presented view.
@@ -111,14 +117,12 @@
 animationControllerForPresentedController:(UIViewController*)presented
                      presentingController:(UIViewController*)presenting
                          sourceController:(UIViewController*)source {
-  return [[[QRScannerTransitionAnimator alloc] initWithTransition:PRESENT]
-      autorelease];
+  return [[QRScannerTransitionAnimator alloc] initWithTransition:PRESENT];
 }
 
 - (id<UIViewControllerAnimatedTransitioning>)
 animationControllerForDismissedController:(UIViewController*)dismissed {
-  return [[[QRScannerTransitionAnimator alloc] initWithTransition:DISMISS]
-      autorelease];
+  return [[QRScannerTransitionAnimator alloc] initWithTransition:DISMISS];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h
index 1cc8598..1ceb8bc 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h
@@ -24,7 +24,7 @@
 @interface QRScannerView : UIView
 
 // The delegate which receives button events.
-@property(nonatomic, assign) id<QRScannerViewDelegate> delegate;
+@property(nonatomic, weak) id<QRScannerViewDelegate> delegate;
 
 - (instancetype)initWithFrame:(CGRect)frame
                      delegate:(id<QRScannerViewDelegate>)delegate
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.mm
index 7c01a25..41515a2 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view.mm
@@ -6,7 +6,6 @@
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
-#include "base/mac/scoped_nsobject.h"
 #include "ios/chrome/browser/ui/icons/chrome_icon.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -14,6 +13,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // Padding for buttons in the QR scanner UI.
@@ -108,12 +111,12 @@
   // Creates a transparent preview overlay. The overlay is a sublayer of the
   // PreviewOverlayView's view to keep the opacity of the view's layer 1.0,
   // otherwise the viewport border would inherit the opacity of the overlay.
-  base::scoped_nsobject<CALayer> _previewOverlay;
+  CALayer* _previewOverlay;
   // A container for the viewport border to draw a shadow under the border.
   // Sublayer of PreviewOverlayView's layer.
-  base::scoped_nsobject<CALayer> _viewportBorderContainer;
+  CALayer* _viewportBorderContainer;
   // The preview viewport border. Sublayer of |_viewportBorderContainer|.
-  base::scoped_nsobject<CAShapeLayer> _viewportBorder;
+  CAShapeLayer* _viewportBorder;
 }
 
 // Creates a square mask for the overlay to keep the viewport transparent.
@@ -133,12 +136,12 @@
     return nil;
   }
 
-  _previewOverlay.reset([[CALayer alloc] init]);
+  _previewOverlay = [[CALayer alloc] init];
   [_previewOverlay setBackgroundColor:[[UIColor blackColor] CGColor]];
   [_previewOverlay setOpacity:kPreviewOverlayOpacity];
   [[self layer] addSublayer:_previewOverlay];
 
-  _viewportBorderContainer.reset([[CALayer alloc] init]);
+  _viewportBorderContainer = [[CALayer alloc] init];
   [_viewportBorderContainer setShadowColor:[[UIColor blackColor] CGColor]];
   [_viewportBorderContainer setShadowOffset:CGSizeZero];
   [_viewportBorderContainer setShadowRadius:kViewportBorderShadowRadius];
@@ -147,7 +150,7 @@
   [_viewportBorderContainer
       setRasterizationScale:[[UIScreen mainScreen] scale]];
 
-  _viewportBorder.reset([[CAShapeLayer alloc] init]);
+  _viewportBorder = [[CAShapeLayer alloc] init];
   [_viewportBorder setStrokeColor:[[UIColor whiteColor] CGColor]];
   [_viewportBorder setFillColor:nil];
   [_viewportBorder setOpacity:1.0];
@@ -186,7 +189,7 @@
   UIBezierPath* maskPath = [UIBezierPath bezierPathWithRect:frameRect];
   [maskPath appendPath:[UIBezierPath bezierPathWithRect:viewportRect]];
 
-  CAShapeLayer* mask = [[[CAShapeLayer alloc] init] autorelease];
+  CAShapeLayer* mask = [[CAShapeLayer alloc] init];
   [mask setFillColor:[[UIColor blackColor] CGColor]];
   [mask setFillRule:kCAFillRuleEvenOdd];
   [mask setFrame:frameRect];
@@ -215,7 +218,7 @@
   [path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(offsetX, offsetY,
                                                                sizeX, sizeY)]];
 
-  CAShapeLayer* mask = [[[CAShapeLayer alloc] init] autorelease];
+  CAShapeLayer* mask = [[CAShapeLayer alloc] init];
   [mask setFillColor:[[UIColor blackColor] CGColor]];
   [mask setFrame:CGRectMake(0, 0, frameSize.width, frameSize.height)];
   [mask setPath:path.CGPath];
@@ -226,19 +229,19 @@
 
 @interface QRScannerView () {
   // A button to toggle the torch.
-  base::scoped_nsobject<MDCFlatButton> _torchButton;
+  MDCFlatButton* _torchButton;
   // A view containing the preview layer for camera input.
-  base::scoped_nsobject<VideoPreviewView> _previewView;
+  VideoPreviewView* _previewView;
   // A transparent overlay on top of the preview layer.
-  base::scoped_nsobject<PreviewOverlayView> _previewOverlay;
+  PreviewOverlayView* _previewOverlay;
   // The constraint specifying that the preview overlay should be square.
-  base::scoped_nsobject<NSLayoutConstraint> _overlaySquareConstraint;
+  NSLayoutConstraint* _overlaySquareConstraint;
   // The constraint relating the size of the |_previewOverlay| to the width of
   // the QRScannerView.
-  base::scoped_nsobject<NSLayoutConstraint> _overlayWidthConstraint;
+  NSLayoutConstraint* _overlayWidthConstraint;
   // The constraint relating the size of the |_previewOverlay| to the height of
   // te QRScannerView.
-  base::scoped_nsobject<NSLayoutConstraint> _overlayHeightConstraint;
+  NSLayoutConstraint* _overlayHeightConstraint;
 }
 
 // Creates an image with template rendering mode for use in icons.
@@ -372,7 +375,7 @@
 }
 
 - (void)animateScanningResultWithCompletion:(void (^)(void))completion {
-  UIView* whiteView = [[[UIView alloc] init] autorelease];
+  UIView* whiteView = [[UIView alloc] init];
   whiteView.frame = self.bounds;
   [self addSubview:whiteView];
   whiteView.backgroundColor = [UIColor whiteColor];
@@ -420,8 +423,7 @@
 }
 
 - (void)addCloseButton {
-  MDCFlatButton* closeButton =
-      [[[MDCFlatButton alloc] initWithFrame:CGRectZero] autorelease];
+  MDCFlatButton* closeButton = [[MDCFlatButton alloc] initWithFrame:CGRectZero];
   UIImage* closeIcon = [ChromeIcon closeIcon];
   UIImage* closeButtonIcon =
       [closeIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
@@ -443,7 +445,7 @@
 
 - (void)addTorchButton {
   DCHECK(!_torchButton);
-  _torchButton.reset([[MDCFlatButton alloc] initWithFrame:CGRectZero]);
+  _torchButton = [[MDCFlatButton alloc] initWithFrame:CGRectZero];
   [_torchButton setEnabled:NO];
   [self configureButton:_torchButton
                withIcon:[self torchOffIcon]
@@ -467,7 +469,7 @@
 }
 
 - (void)addViewportCaptionLabel {
-  UILabel* viewportCaption = [[[UILabel alloc] init] autorelease];
+  UILabel* viewportCaption = [[UILabel alloc] init];
   NSString* label = l10n_util::GetNSString(IDS_IOS_QR_SCANNER_VIEWPORT_CAPTION);
   [viewportCaption setText:label];
   [viewportCaption setNumberOfLines:0];
@@ -501,37 +503,37 @@
 
 - (void)setupPreviewView {
   DCHECK(!_previewView);
-  _previewView.reset([[VideoPreviewView alloc] initWithFrame:self.frame]);
+  _previewView = [[VideoPreviewView alloc] initWithFrame:self.frame];
   [self insertSubview:_previewView atIndex:0];
 }
 
 - (void)setupPreviewOverlayView {
   DCHECK(!_previewOverlay);
-  _previewOverlay.reset([[PreviewOverlayView alloc] initWithFrame:CGRectZero]);
+  _previewOverlay = [[PreviewOverlayView alloc] initWithFrame:CGRectZero];
   [self addSubview:_previewOverlay];
 
   // Add a multiplier of sqrt(2) to the width and height constraints to make
   // sure that the overlay covers the whole screen during rotation.
-  _overlayWidthConstraint.reset(
-      [[NSLayoutConstraint constraintWithItem:_previewOverlay
-                                    attribute:NSLayoutAttributeWidth
-                                    relatedBy:NSLayoutRelationGreaterThanOrEqual
-                                       toItem:self
-                                    attribute:NSLayoutAttributeWidth
-                                   multiplier:sqrt(2)
-                                     constant:0.0] retain]);
+  _overlayWidthConstraint =
+      [NSLayoutConstraint constraintWithItem:_previewOverlay
+                                   attribute:NSLayoutAttributeWidth
+                                   relatedBy:NSLayoutRelationGreaterThanOrEqual
+                                      toItem:self
+                                   attribute:NSLayoutAttributeWidth
+                                  multiplier:sqrt(2)
+                                    constant:0.0];
 
-  _overlayHeightConstraint.reset(
-      [[NSLayoutConstraint constraintWithItem:_previewOverlay
-                                    attribute:NSLayoutAttributeHeight
-                                    relatedBy:NSLayoutRelationGreaterThanOrEqual
-                                       toItem:self
-                                    attribute:NSLayoutAttributeHeight
-                                   multiplier:sqrt(2)
-                                     constant:0.0] retain]);
+  _overlayHeightConstraint =
+      [NSLayoutConstraint constraintWithItem:_previewOverlay
+                                   attribute:NSLayoutAttributeHeight
+                                   relatedBy:NSLayoutRelationGreaterThanOrEqual
+                                      toItem:self
+                                   attribute:NSLayoutAttributeHeight
+                                  multiplier:sqrt(2)
+                                    constant:0.0];
 
-  _overlaySquareConstraint.reset([[[_previewOverlay heightAnchor]
-      constraintEqualToAnchor:[_previewOverlay widthAnchor]] retain]);
+  _overlaySquareConstraint = [[_previewOverlay heightAnchor]
+      constraintEqualToAnchor:[_previewOverlay widthAnchor]];
 
   // Constrains the preview overlay to be square, centered, with both width and
   // height greater than or equal to the width and height of the QRScannerView.
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h
index 95550fc6..973b6ef 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h
@@ -26,7 +26,7 @@
 
 // The delegate which receives the scanned result after the view controller is
 // dismissed.
-@property(nonatomic, assign) id<QRScannerViewControllerDelegate> delegate;
+@property(nonatomic, weak) id<QRScannerViewControllerDelegate> delegate;
 
 - (instancetype)initWithDelegate:(id<QRScannerViewControllerDelegate>)delegate
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.mm
index b98f7d1..99912a1 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.mm
@@ -7,7 +7,6 @@
 #import <AVFoundation/AVFoundation.h>
 
 #include "base/logging.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_alerts.h"
@@ -16,6 +15,10 @@
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using base::UserMetricsAction;
 
 namespace {
@@ -34,16 +37,16 @@
 
 @interface QRScannerViewController ()<QRScannerViewDelegate> {
   // The CameraController managing the camera connection.
-  base::scoped_nsobject<CameraController> _cameraController;
+  CameraController* _cameraController;
   // The view displaying the QR scanner.
-  base::scoped_nsobject<QRScannerView> _qrScannerView;
+  QRScannerView* _qrScannerView;
   // The scanned result.
-  base::scoped_nsobject<NSString> _result;
+  NSString* _result;
   // Whether the scanned result should be immediately loaded.
   BOOL _loadResultImmediately;
   // The transitioning delegate used for presenting and dismissing the QR
   // scanner.
-  base::scoped_nsobject<QRScannerTransitioningDelegate> _transitioningDelegate;
+  QRScannerTransitioningDelegate* _transitioningDelegate;
 }
 
 // Dismisses the QRScannerViewController and runs |completion| on completion.
@@ -78,7 +81,7 @@
   if (self) {
     DCHECK(delegate);
     _delegate = delegate;
-    _cameraController.reset([[CameraController alloc] initWithDelegate:self]);
+    _cameraController = [[CameraController alloc] initWithDelegate:self];
   }
   return self;
 }
@@ -106,8 +109,8 @@
   [super viewDidLoad];
   DCHECK(_cameraController);
 
-  _qrScannerView.reset(
-      [[QRScannerView alloc] initWithFrame:self.view.frame delegate:self]);
+  _qrScannerView =
+      [[QRScannerView alloc] initWithFrame:self.view.frame delegate:self];
   [self.view addSubview:_qrScannerView];
 
   // Constraints for |_qrScannerView|.
@@ -205,8 +208,7 @@
   switch ([_cameraController getAuthorizationStatus]) {
     case AVAuthorizationStatusNotDetermined:
     case AVAuthorizationStatusAuthorized:
-      _transitioningDelegate.reset(
-          [[QRScannerTransitioningDelegate alloc] init]);
+      _transitioningDelegate = [[QRScannerTransitioningDelegate alloc] init];
       [self setTransitioningDelegate:_transitioningDelegate];
       return self;
     case AVAuthorizationStatusRestricted:
@@ -300,7 +302,7 @@
     case qr_scanner::CAMERA_IN_USE_BY_ANOTHER_APPLICATION:
     case qr_scanner::MULTIPLE_FOREGROUND_APPS:
     case qr_scanner::CAMERA_PERMISSION_DENIED:
-    case qr_scanner::CAMERA_UNAVAILABLE:
+    case qr_scanner::CAMERA_UNAVAILABLE: {
       // Dismiss any presented alerts.
       if ([self presentedViewController]) {
         [self dismissViewControllerAnimated:YES completion:nil];
@@ -314,6 +316,7 @@
                          animated:YES
                        completion:nil];
       break;
+    }
     case qr_scanner::CAMERA_NOT_LOADED:
       NOTREACHED();
       break;
@@ -333,7 +336,7 @@
     // Post a notification announcing that a code was scanned. QR scanner will
     // be dismissed when the UIAccessibilityAnnouncementDidFinishNotification is
     // received.
-    _result.reset([result copy]);
+    _result = [result copy];
     _loadResultImmediately = load;
     UIAccessibilityPostNotification(
         UIAccessibilityAnnouncementNotification,
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index b0d00a74..de52bb0 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -607,6 +607,7 @@
     "web_state/crw_pass_kit_downloader_unittest.mm",
     "web_state/error_translation_util_unittest.mm",
     "web_state/js/common_js_unittest.mm",
+    "web_state/js/context_menu_js_unittest.mm",
     "web_state/js/core_js_unittest.mm",
     "web_state/js/credential_util_unittest.mm",
     "web_state/js/crw_js_injection_manager_unittest.mm",
@@ -696,7 +697,10 @@
     "web_state/js/resources/context_menu.js",
     "web_state/js/resources/core.js",
     "web_state/js/resources/dialog_overrides.js",
+    "web_state/js/resources/form.js",
     "web_state/js/resources/message.js",
+    "web_state/js/resources/navigation.js",
+    "web_state/js/resources/scroll_workaround.js",
     "web_state/js/resources/web_bundle.js",
   ]
 }
diff --git a/ios/web/web_state/js/common_js_unittest.mm b/ios/web/web_state/js/common_js_unittest.mm
index 5e10f768..42505ce 100644
--- a/ios/web/web_state/js/common_js_unittest.mm
+++ b/ios/web/web_state/js/common_js_unittest.mm
@@ -6,6 +6,7 @@
 #import <Foundation/Foundation.h>
 
 #include "base/macros.h"
+#include "base/strings/sys_string_conversions.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
@@ -22,6 +23,12 @@
   const bool expected_is_text_field;
 };
 
+// Struct for stringify() test data.
+struct TestScriptAndExpectedValue {
+  NSString* test_script;
+  id expected_value;
+};
+
 }  // namespace
 
 namespace web {
@@ -87,4 +94,48 @@
   }
 }
 
+// Tests __gCrWeb.stringify JavaScript API.
+TEST_F(CommonJsTest, Stringify) {
+  TestScriptAndExpectedValue test_data[] = {
+      // Stringify a string that contains various characters that must
+      // be escaped.
+      {@"__gCrWeb.stringify('a\\u000a\\t\\b\\\\\\\"Z')",
+       @"\"a\\n\\t\\b\\\\\\\"Z\""},
+      // Stringify a number.
+      {@"__gCrWeb.stringify(77.7)", @"77.7"},
+      // Stringify an array.
+      {@"__gCrWeb.stringify(['a','b'])", @"[\"a\",\"b\"]"},
+      // Stringify an object.
+      {@"__gCrWeb.stringify({'a':'b','c':'d'})", @"{\"a\":\"b\",\"c\":\"d\"}"},
+      // Stringify a hierarchy of objects and arrays.
+      {@"__gCrWeb.stringify([{'a':['b','c'],'d':'e'},'f'])",
+       @"[{\"a\":[\"b\",\"c\"],\"d\":\"e\"},\"f\"]"},
+      // Stringify null.
+      {@"__gCrWeb.stringify(null)", @"null"},
+      // Stringify an object with a toJSON function.
+      {@"temp = [1,2];"
+        "temp.toJSON = function (key) {return undefined};"
+        "__gCrWeb.stringify(temp)",
+       @"[1,2]"},
+      // Stringify an object with a toJSON property that is not a function.
+      {@"temp = [1,2];"
+        "temp.toJSON = 42;"
+        "__gCrWeb.stringify(temp)",
+       @"[1,2]"},
+      // Stringify an undefined object.
+      {@"__gCrWeb.stringify(undefined)", @"undefined"},
+  };
+
+  for (size_t i = 0; i < arraysize(test_data); i++) {
+    TestScriptAndExpectedValue& data = test_data[i];
+    // Load a sample HTML page. As a side-effect, loading HTML via
+    // |webController_| will also inject core.js.
+    LoadHtml(@"<p>");
+    id result = ExecuteJavaScript(data.test_script);
+    EXPECT_NSEQ(data.expected_value, result)
+        << " in test " << i << ": "
+        << base::SysNSStringToUTF8(data.test_script);
+  }
+}
+
 }  // namespace web
diff --git a/ios/web/web_state/js/context_menu_js_unittest.mm b/ios/web/web_state/js/context_menu_js_unittest.mm
new file mode 100644
index 0000000..b3dfed3d2
--- /dev/null
+++ b/ios/web/web_state/js/context_menu_js_unittest.mm
@@ -0,0 +1,199 @@
+// 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 <vector>
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#import "ios/web/public/test/web_test_with_web_state.h"
+#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
+#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
+#import "ios/web/public/web_state/web_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+// Unit tests for ios/web/web_state/js/resources/context_menu.js.
+
+namespace {
+
+// Test coordinates and expected result for __gCrWeb.getElementFromPoint call.
+struct TestCoordinatesAndExpectedValue {
+  TestCoordinatesAndExpectedValue(CGFloat x, CGFloat y, id expected_value)
+      : x(x), y(y), expected_value(expected_value) {}
+  CGFloat x = 0;
+  CGFloat y = 0;
+  id expected_value = nil;
+};
+
+}  // namespace
+
+namespace web {
+
+// Test fixture to test context_menu.js.
+class ContextMenuJsTest : public web::WebTestWithWebState {
+ protected:
+  // Verifies that __gCrWeb.getElementFromPoint returns |expected_value| for
+  // the given image |html|.
+  void ImageTesterHelper(NSString* html, NSDictionary* expected_value) {
+    NSString* page_content_template =
+        @"<html><body style='margin-left:10px;margin-top:10px;'>"
+         "<div style='width:100px;height:100px;'>"
+         "  <p style='position:absolute;left:25px;top:25px;"
+         "      width:50px;height:50px'>"
+         "%@"
+         "    Chrome rocks!"
+         "  </p></div></body></html>";
+    NSString* page_content =
+        [NSString stringWithFormat:page_content_template, html];
+
+    TestCoordinatesAndExpectedValue test_data[] = {
+        // Point outside the document margins.
+        {0, 0, @{}},
+        // Point inside the <p> element.
+        {20, 20, expected_value},
+        // Point outside the <p> element.
+        {GetWebViewContentSize().width / 2, 50, @{}},
+    };
+    for (size_t i = 0; i < arraysize(test_data); i++) {
+      const TestCoordinatesAndExpectedValue& data = test_data[i];
+      LoadHtml(page_content);
+      id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
+      EXPECT_NSEQ(data.expected_value, result)
+          << " in test " << i << ": (" << data.x << ", " << data.y << ")";
+    }
+  }
+  // Returns web view's content size from the current web state.
+  CGSize GetWebViewContentSize() {
+    return web_state()->GetWebViewProxy().scrollViewProxy.contentSize;
+  }
+
+  // Executes __gCrWeb.getElementFromPoint script and syncronously returns the
+  // result. |x| and |y| are points in web view coordinate system.
+  id ExecuteGetElementFromPointJavaScript(CGFloat x, CGFloat y) {
+    CGSize contentSize = GetWebViewContentSize();
+    NSString* const script = [NSString
+        stringWithFormat:@"__gCrWeb.getElementFromPoint(%g, %g, %g, %g)", x, y,
+                         contentSize.width, contentSize.height];
+    return ExecuteJavaScript(script);
+  }
+};
+
+// Tests that __gCrWeb.getElementFromPoint function returns correct src.
+TEST_F(ContextMenuJsTest, GetImageUrlAtPoint) {
+  NSString* html =
+      @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
+  NSDictionary* expected_value = @{
+    @"src" : @"file:///bogus",
+    @"referrerPolicy" : @"default",
+  };
+  ImageTesterHelper(html, expected_value);
+}
+
+// Tests that __gCrWeb.getElementFromPoint function returns correct title.
+TEST_F(ContextMenuJsTest, GetImageTitleAtPoint) {
+  NSString* html = @"<img id='foo' title='Hello world!'"
+                    "style='width:200;height:200;' src='file:///bogus'/>";
+  NSDictionary* expected_value = @{
+    @"src" : @"file:///bogus",
+    @"referrerPolicy" : @"default",
+    @"title" : @"Hello world!",
+  };
+  ImageTesterHelper(html, expected_value);
+}
+
+// Tests that __gCrWeb.getElementFromPoint function returns correct href.
+TEST_F(ContextMenuJsTest, GetLinkImageUrlAtPoint) {
+  NSString* html =
+      @"<a href='file:///linky'>"
+       "<img id='foo' style='width:200;height:200;' src='file:///bogus'/>"
+       "</a>";
+  NSDictionary* expected_value = @{
+    @"src" : @"file:///bogus",
+    @"referrerPolicy" : @"default",
+    @"href" : @"file:///linky",
+  };
+  ImageTesterHelper(html, expected_value);
+}
+
+TEST_F(ContextMenuJsTest, TextAreaStopsProximity) {
+  NSString* html =
+      @"<html><body style='margin-left:10px;margin-top:10px;'>"
+       "<div style='width:100px;height:100px;'>"
+       "<img id='foo'"
+       "    style='position:absolute;left:0px;top:0px;width:50px;height:50px'"
+       "    src='file:///bogus' />"
+       "<input type='text' name='name'"
+       "       style='position:absolute;left:5px;top:5px; "
+       "width:40px;height:40px'/>"
+       "</div></body> </html>";
+
+  NSDictionary* success = @{
+    @"src" : @"file:///bogus",
+    @"referrerPolicy" : @"default",
+  };
+  NSDictionary* failure = @{};
+
+  TestCoordinatesAndExpectedValue test_data[] = {
+      {2, 20, success}, {10, 10, failure},
+  };
+
+  for (size_t i = 0; i < arraysize(test_data); i++) {
+    const TestCoordinatesAndExpectedValue& data = test_data[i];
+    LoadHtml(html);
+    id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
+    EXPECT_NSEQ(data.expected_value, result)
+        << " in test " << i << ": (" << data.x << ", " << data.y << ")";
+  }
+}
+
+// Tests the javascript of the url of the an image present in the DOM.
+TEST_F(ContextMenuJsTest, LinkOfImage) {
+  // A page with a large image surrounded by a link.
+  static const char image[] =
+      "<a href='%s'><img width=400 height=400 src='foo'></img></a>";
+
+  // A page with a link to a destination URL.
+  LoadHtml(base::StringPrintf(image, "http://destination"));
+  id result = ExecuteGetElementFromPointJavaScript(20, 20);
+  NSDictionary* expected_result = @{
+    @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
+    @"referrerPolicy" : @"default",
+    @"href" : @"http://destination/",
+  };
+  EXPECT_NSEQ(expected_result, result);
+
+  // A page with a link with some JavaScript that does not result in a NOP.
+  LoadHtml(base::StringPrintf(image, "javascript:console.log('whatever')"));
+  result = ExecuteGetElementFromPointJavaScript(20, 20);
+  expected_result = @{
+    @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
+    @"referrerPolicy" : @"default",
+    @"href" : @"javascript:console.log(",
+  };
+  EXPECT_NSEQ(expected_result, result);
+
+  // A list of JavaScripts that result in a NOP.
+  std::vector<std::string> nop_javascripts;
+  nop_javascripts.push_back(";");
+  nop_javascripts.push_back("void(0);");
+  nop_javascripts.push_back("void(0);  void(0); void(0)");
+
+  for (auto js : nop_javascripts) {
+    // A page with a link with some JavaScript that results in a NOP.
+    const std::string javascript = std::string("javascript:") + js;
+    LoadHtml(base::StringPrintf(image, javascript.c_str()));
+    result = ExecuteGetElementFromPointJavaScript(20, 20);
+    expected_result = @{
+      @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
+      @"referrerPolicy" : @"default",
+    };
+    // Make sure the returned JSON does not have an 'href' key.
+    EXPECT_NSEQ(expected_result, result);
+  }
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/js/core_js_unittest.mm b/ios/web/web_state/js/core_js_unittest.mm
index 46e2703..7420220 100644
--- a/ios/web/web_state/js/core_js_unittest.mm
+++ b/ios/web/web_state/js/core_js_unittest.mm
@@ -4,157 +4,19 @@
 
 #include <vector>
 
-#import <CoreGraphics/CoreGraphics.h>
-#import <Foundation/Foundation.h>
-
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
-#import "base/strings/sys_string_conversions.h"
+#include "base/strings/sys_string_conversions.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
-#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
-#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
-#import "ios/web/public/web_state/web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#import "testing/gtest_mac.h"
+#include "testing/gtest_mac.h"
 
 // Unit tests for ios/web/web_state/js/resources/core.js.
 
-namespace {
-
-struct TestScriptAndExpectedValue {
-  NSString* test_script;
-  id expected_value;
-};
-
-// Test coordinates and expected result for __gCrWeb.getElementFromPoint call.
-struct TestCoordinatesAndExpectedValue {
-  TestCoordinatesAndExpectedValue(CGFloat x, CGFloat y, id expected_value)
-      : x(x), y(y), expected_value(expected_value) {}
-  CGFloat x = 0;
-  CGFloat y = 0;
-  id expected_value = nil;
-};
-
-}  // namespace
-
 namespace web {
 
 // Test fixture to test core.js.
-class CoreJsTest : public web::WebTestWithWebState {
- protected:
-  // Verifies that __gCrWeb.getElementFromPoint returns |expected_value| for
-  // the given image |html|.
-  void ImageTesterHelper(NSString* html, NSDictionary* expected_value) {
-    NSString* page_content_template =
-        @"<html><body style='margin-left:10px;margin-top:10px;'>"
-         "<div style='width:100px;height:100px;'>"
-         "  <p style='position:absolute;left:25px;top:25px;"
-         "      width:50px;height:50px'>"
-         "%@"
-         "    Chrome rocks!"
-         "  </p></div></body></html>";
-    NSString* page_content =
-        [NSString stringWithFormat:page_content_template, html];
-
-    TestCoordinatesAndExpectedValue test_data[] = {
-        // Point outside the document margins.
-        {0, 0, @{}},
-        // Point inside the <p> element.
-        {20, 20, expected_value},
-        // Point outside the <p> element.
-        {GetWebViewContentSize().width / 2, 50, @{}},
-    };
-    for (size_t i = 0; i < arraysize(test_data); i++) {
-      const TestCoordinatesAndExpectedValue& data = test_data[i];
-      LoadHtml(page_content);
-      id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
-      EXPECT_NSEQ(data.expected_value, result)
-          << " in test " << i << ": (" << data.x << ", " << data.y << ")";
-    }
-  }
-  // Returns web view's content size from the current web state.
-  CGSize GetWebViewContentSize() {
-    return web_state()->GetWebViewProxy().scrollViewProxy.contentSize;
-  }
-
-  // Executes __gCrWeb.getElementFromPoint script and syncronously returns the
-  // result. |x| and |y| are points in web view coordinate system.
-  id ExecuteGetElementFromPointJavaScript(CGFloat x, CGFloat y) {
-    CGSize contentSize = GetWebViewContentSize();
-    NSString* const script = [NSString
-        stringWithFormat:@"__gCrWeb.getElementFromPoint(%g, %g, %g, %g)", x, y,
-                         contentSize.width, contentSize.height];
-    return ExecuteJavaScript(script);
-  }
-};
-
-// Tests that __gCrWeb.getElementFromPoint function returns correct src.
-TEST_F(CoreJsTest, GetImageUrlAtPoint) {
-  NSString* html =
-      @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
-  NSDictionary* expected_value = @{
-    @"src" : @"file:///bogus",
-    @"referrerPolicy" : @"default",
-  };
-  ImageTesterHelper(html, expected_value);
-}
-
-// Tests that __gCrWeb.getElementFromPoint function returns correct title.
-TEST_F(CoreJsTest, GetImageTitleAtPoint) {
-  NSString* html = @"<img id='foo' title='Hello world!'"
-                    "style='width:200;height:200;' src='file:///bogus'/>";
-  NSDictionary* expected_value = @{
-    @"src" : @"file:///bogus",
-    @"referrerPolicy" : @"default",
-    @"title" : @"Hello world!",
-  };
-  ImageTesterHelper(html, expected_value);
-}
-
-// Tests that __gCrWeb.getElementFromPoint function returns correct href.
-TEST_F(CoreJsTest, GetLinkImageUrlAtPoint) {
-  NSString* html =
-      @"<a href='file:///linky'>"
-       "<img id='foo' style='width:200;height:200;' src='file:///bogus'/>"
-       "</a>";
-  NSDictionary* expected_value = @{
-    @"src" : @"file:///bogus",
-    @"referrerPolicy" : @"default",
-    @"href" : @"file:///linky",
-  };
-  ImageTesterHelper(html, expected_value);
-}
-
-TEST_F(CoreJsTest, TextAreaStopsProximity) {
-  NSString* html =
-      @"<html><body style='margin-left:10px;margin-top:10px;'>"
-       "<div style='width:100px;height:100px;'>"
-       "<img id='foo'"
-       "    style='position:absolute;left:0px;top:0px;width:50px;height:50px'"
-       "    src='file:///bogus' />"
-       "<input type='text' name='name'"
-       "       style='position:absolute;left:5px;top:5px; "
-       "width:40px;height:40px'/>"
-       "</div></body> </html>";
-
-  NSDictionary* success = @{
-    @"src" : @"file:///bogus",
-    @"referrerPolicy" : @"default",
-  };
-  NSDictionary* failure = @{};
-
-  TestCoordinatesAndExpectedValue test_data[] = {
-      {2, 20, success}, {10, 10, failure},
-  };
-
-  for (size_t i = 0; i < arraysize(test_data); i++) {
-    const TestCoordinatesAndExpectedValue& data = test_data[i];
-    LoadHtml(html);
-    id result = ExecuteGetElementFromPointJavaScript(data.x, data.y);
-    EXPECT_NSEQ(data.expected_value, result)
-        << " in test " << i << ": (" << data.x << ", " << data.y << ")";
-  }
-}
+typedef web::WebTestWithWebState CoreJsTest;
 
 struct TestDataForPasswordFormDetection {
   NSString* pageContent;
@@ -194,113 +56,4 @@
       << base::SysNSStringToUTF8(data.pageContent);
 }
 
-TEST_F(CoreJsTest, Stringify) {
-  // TODO(jeanfrancoisg): Test whether __gCrWeb.stringify(undefined) correctly
-  //returns undefined.
-  TestScriptAndExpectedValue testData[] = {
-    // Stringify a string that contains various characters that must
-    // be escaped.
-    {
-      @"__gCrWeb.stringify('a\\u000a\\t\\b\\\\\\\"Z')",
-      @"\"a\\n\\t\\b\\\\\\\"Z\""
-    },
-    // Stringify a number.
-    {
-      @"__gCrWeb.stringify(77.7)",
-      @"77.7"
-    },
-    // Stringify an array.
-    {
-      @"__gCrWeb.stringify(['a','b'])",
-      @"[\"a\",\"b\"]"
-    },
-    // Stringify an object.
-    {
-      @"__gCrWeb.stringify({'a':'b','c':'d'})",
-      @"{\"a\":\"b\",\"c\":\"d\"}"
-    },
-    // Stringify a hierarchy of objects and arrays.
-    {
-      @"__gCrWeb.stringify([{'a':['b','c'],'d':'e'},'f'])",
-      @"[{\"a\":[\"b\",\"c\"],\"d\":\"e\"},\"f\"]"
-    },
-    // Stringify null.
-    {
-      @"__gCrWeb.stringify(null)",
-      @"null"
-    },
-    // Stringify an object with a toJSON function.
-    {
-      @"temp = [1,2];"
-      "temp.toJSON = function (key) {return undefined};"
-      "__gCrWeb.stringify(temp)",
-      @"[1,2]"
-    },
-    // Stringify an object with a toJSON property that is not a function.
-    {
-      @"temp = [1,2];"
-      "temp.toJSON = 42;"
-      "__gCrWeb.stringify(temp)",
-      @"[1,2]"
-    },
-  };
-
-  for (size_t i = 0; i < arraysize(testData); i++) {
-    TestScriptAndExpectedValue& data = testData[i];
-    // Load a sample HTML page. As a side-effect, loading HTML via
-    // |webController_| will also inject core.js.
-    LoadHtml(@"<p>");
-    id result = ExecuteJavaScript(data.test_script);
-    EXPECT_NSEQ(data.expected_value, result)
-        << " in test " << i << ": "
-        << base::SysNSStringToUTF8(data.test_script);
-  }
-}
-
-// Tests the javascript of the url of the an image present in the DOM.
-TEST_F(CoreJsTest, LinkOfImage) {
-  // A page with a large image surrounded by a link.
-  static const char image[] =
-      "<a href='%s'><img width=400 height=400 src='foo'></img></a>";
-
-  // A page with a link to a destination URL.
-  LoadHtml(base::StringPrintf(image, "http://destination"));
-  id result = ExecuteGetElementFromPointJavaScript(20, 20);
-  NSDictionary* expected_result = @{
-    @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
-    @"referrerPolicy" : @"default",
-    @"href" : @"http://destination/",
-  };
-  EXPECT_NSEQ(expected_result, result);
-
-  // A page with a link with some JavaScript that does not result in a NOP.
-  LoadHtml(base::StringPrintf(image, "javascript:console.log('whatever')"));
-  result = ExecuteGetElementFromPointJavaScript(20, 20);
-  expected_result = @{
-    @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
-    @"referrerPolicy" : @"default",
-    @"href" : @"javascript:console.log(",
-  };
-  EXPECT_NSEQ(expected_result, result);
-
-  // A list of JavaScripts that result in a NOP.
-  std::vector<std::string> nop_javascripts;
-  nop_javascripts.push_back(";");
-  nop_javascripts.push_back("void(0);");
-  nop_javascripts.push_back("void(0);  void(0); void(0)");
-
-  for (auto js : nop_javascripts) {
-    // A page with a link with some JavaScript that results in a NOP.
-    const std::string javascript = std::string("javascript:") + js;
-    LoadHtml(base::StringPrintf(image, javascript.c_str()));
-    result = ExecuteGetElementFromPointJavaScript(20, 20);
-    expected_result = @{
-      @"src" : [NSString stringWithFormat:@"%sfoo", BaseUrl().c_str()],
-      @"referrerPolicy" : @"default",
-    };
-    // Make sure the returned JSON does not have an 'href' key.
-    EXPECT_NSEQ(expected_result, result);
-  }
-}
-
 }  // namespace web
diff --git a/ios/web/web_state/js/resources/common.js b/ios/web/web_state/js/resources/common.js
index 56adb250ff..81d0df14 100644
--- a/ios/web/web_state/js/resources/common.js
+++ b/ios/web/web_state/js/resources/common.js
@@ -26,6 +26,7 @@
 
 /* Beginning of anonymous object. */
 (function() {
+
   /**
    * JSON safe object to protect against custom implementation of Object.toJSON
    * in host pages.
@@ -46,6 +47,31 @@
   __gCrWeb.common.JSONStringify = JSON.stringify;
 
   /**
+   * Returns a string that is formatted according to the JSON syntax rules.
+   * This is equivalent to the built-in JSON.stringify() function, but is
+   * less likely to be overridden by the website itself.  Prefer the private
+   * {@code __gcrWeb.common.JSONStringify} whenever possible instead of using
+   * this public function. The |__gCrWeb| object itself does not use it; it uses
+   * its private counterpart instead.
+   */
+  __gCrWeb['stringify'] = function(value) {
+    if (value === null)
+      return 'null';
+    if (value === undefined)
+       return 'undefined';
+    if (typeof(value.toJSON) == 'function') {
+      // Prevents websites from changing stringify's behavior by adding the
+      // method toJSON() by temporarily removing it.
+      var originalToJSON = value.toJSON;
+      value.toJSON = undefined;
+      var stringifiedValue = __gCrWeb.common.JSONStringify(value);
+      value.toJSON = originalToJSON;
+      return stringifiedValue;
+    }
+    return __gCrWeb.common.JSONStringify(value);
+  };
+
+  /**
    * Prefix used in references to form elements that have no 'id' or 'name'
    */
   __gCrWeb.common.kNamelessFormIDPrefix = 'gChrome~';
@@ -689,4 +715,5 @@
     }
     return false;
   };
+
 }());  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js
index 0a477eb..878481a8 100644
--- a/ios/web/web_state/js/resources/core.js
+++ b/ios/web/web_state/js/resources/core.js
@@ -36,8 +36,8 @@
   window.addEventListener('error', function(event) {
     // Sadly, event.filename and event.lineno are always 'undefined' and '0'
     // with UIWebView.
-    invokeOnHost_({'command': 'window.error',
-                   'message': event.message.toString()});
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.error', 'message': event.message.toString()});
   });
 
 
@@ -47,66 +47,6 @@
     return hasPasswordField_(window);
   };
 
-  // Returns a string that is formatted according to the JSON syntax rules.
-  // This is equivalent to the built-in JSON.stringify() function, but is
-  // less likely to be overridden by the website itself.  This public function
-  // should not be used if spoofing it would create a security vulnerability.
-  // The |__gCrWeb| object itself does not use it; it uses its private
-  // counterpart instead.
-  // Prevents websites from changing stringify's behavior by adding the
-  // method toJSON() by temporarily removing it.
-  __gCrWeb['stringify'] = function(value) {
-    if (value === null)
-      return 'null';
-    if (value === undefined)
-      return undefined;
-    if (typeof(value.toJSON) == 'function') {
-      var originalToJSON = value.toJSON;
-      value.toJSON = undefined;
-      var stringifiedValue = __gCrWeb.common.JSONStringify(value);
-      value.toJSON = originalToJSON;
-      return stringifiedValue;
-    }
-    return __gCrWeb.common.JSONStringify(value);
-  };
-
-  /*
-   * Adds the listeners that are used to handle forms, enabling autofill and
-   * the replacement method to dismiss the keyboard needed because of the
-   * Autofill keyboard accessory.
-   */
-  function addFormEventListeners_() {
-    // Focus and input events for form elements are messaged to the main
-    // application for broadcast to CRWWebControllerObservers.
-    // This is done with a single event handler for each type being added to the
-    // main document element which checks the source element of the event; this
-    // is much easier to manage than adding handlers to individual elements.
-    var formActivity = function(evt) {
-      var srcElement = evt.srcElement;
-      var fieldName = srcElement.name || '';
-      var value = srcElement.value || '';
-
-      var msg = {
-        'command': 'form.activity',
-        'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement.form),
-        'fieldName': fieldName,
-        'type': evt.type,
-        'value': value
-      };
-      invokeOnHost_(msg);
-    };
-
-    // Focus events performed on the 'capture' phase otherwise they are often
-    // not received.
-    document.addEventListener('focus', formActivity, true);
-    document.addEventListener('blur', formActivity, true);
-    document.addEventListener('change', formActivity, true);
-
-    // Text input is watched at the bubbling phase as this seems adequate in
-    // practice and it is less obtrusive to page scripts than capture phase.
-    document.addEventListener('input', formActivity, false);
-    document.addEventListener('keyup', formActivity, false);
-  };
 
   // Returns true if the supplied window or any frames inside contain an input
   // field of type 'password'.
@@ -134,149 +74,14 @@
     return false;
   };
 
-  function invokeOnHost_(command) {
-    __gCrWeb.message.invokeOnHost(command);
-  };
-
-  // Various aspects of global DOM behavior are overridden here.
-
-  // A popstate event needs to be fired anytime the active history entry
-  // changes without an associated document change. Either via back, forward, go
-  // navigation or by loading the URL, clicking on a link, etc.
-  __gCrWeb['dispatchPopstateEvent'] = function(stateObject) {
-    var popstateEvent = window.document.createEvent('HTMLEvents');
-    popstateEvent.initEvent('popstate', true, false);
-    if (stateObject)
-      popstateEvent.state = JSON.parse(stateObject);
-
-    // setTimeout() is used in order to return immediately. Otherwise the
-    // dispatchEvent call waits for all event handlers to return, which could
-    // cause a ReentryGuard failure.
-    window.setTimeout(function() {
-      window.dispatchEvent(popstateEvent);
-    }, 0);
-  };
-
-  // A hashchange event needs to be fired after a same-document history
-  // navigation between two URLs that are equivalent except for their fragments.
-  __gCrWeb['dispatchHashchangeEvent'] = function(oldURL, newURL) {
-    var hashchangeEvent = window.document.createEvent('HTMLEvents');
-    hashchangeEvent.initEvent('hashchange', true, false);
-    if (oldURL)
-      hashchangeEvent.oldURL = oldURL;
-    if (newURL)
-      hashchangeEvent.newURL = newURL
-
-    // setTimeout() is used in order to return immediately. Otherwise the
-    // dispatchEvent call waits for all event handlers to return, which could
-    // cause a ReentryGuard failure.
-    window.setTimeout(function() {
-      window.dispatchEvent(hashchangeEvent);
-    }, 0);
-  };
-
-  // Keep the original pushState() and replaceState() methods. It's needed to
-  // update the web view's URL and window.history.state property during history
-  // navigations that don't cause a page load.
-  var originalWindowHistoryPushState = window.history.pushState;
-  var originalWindowHistoryReplaceState = window.history.replaceState;
-  __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
-    originalWindowHistoryReplaceState.call(history, stateObject, '', url);
-  };
-
-  // Intercept window.history methods to call back/forward natively.
-  window.history.back = function() {
-    invokeOnHost_({'command': 'window.history.back'});
-  };
-  window.history.forward = function() {
-    invokeOnHost_({'command': 'window.history.forward'});
-  };
-  window.history.go = function(delta) {
-    invokeOnHost_({'command': 'window.history.go', 'value': delta | 0});
-  };
-  window.history.pushState = function(stateObject, pageTitle, pageUrl) {
-    __gCrWeb.message.invokeOnHost(
-        {'command': 'window.history.willChangeState'});
-    // Calling stringify() on undefined causes a JSON parse error.
-    var serializedState =
-        typeof(stateObject) == 'undefined' ? '' :
-            __gCrWeb.common.JSONStringify(stateObject);
-    pageUrl = pageUrl || window.location.href;
-    originalWindowHistoryPushState.call(history, stateObject,
-                                        pageTitle, pageUrl);
-    invokeOnHost_({'command': 'window.history.didPushState',
-                   'stateObject': serializedState,
-                   'baseUrl': document.baseURI,
-                   'pageUrl': pageUrl.toString()});
-  };
-  window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
-    __gCrWeb.message.invokeOnHost(
-        {'command': 'window.history.willChangeState'});
-
-    // Calling stringify() on undefined causes a JSON parse error.
-    var serializedState =
-        typeof(stateObject) == 'undefined' ? '' :
-            __gCrWeb.common.JSONStringify(stateObject);
-    pageUrl = pageUrl || window.location.href;
-    originalWindowHistoryReplaceState.call(history, stateObject,
-                                           pageTitle, pageUrl);
-    invokeOnHost_({'command': 'window.history.didReplaceState',
-                   'stateObject': serializedState,
-                   'baseUrl': document.baseURI,
-                   'pageUrl': pageUrl.toString()});
-  };
-
-  __gCrWeb['getFullyQualifiedURL'] = function(originalURL) {
-    // A dummy anchor (never added to the document) is used to obtain the
-    // fully-qualified URL of |originalURL|.
-    var anchor = document.createElement('a');
-    anchor.href = originalURL;
-    return anchor.href;
-  };
-
   __gCrWeb['sendFaviconsToHost'] = function() {
     __gCrWeb.message.invokeOnHost({'command': 'document.favicons',
                                    'favicons': __gCrWeb.common.getFavicons()});
   }
 
-  // Tracks whether user is in the middle of scrolling/dragging. If user is
-  // scrolling, ignore window.scrollTo() until user stops scrolling.
-  var webViewScrollViewIsDragging_ = false;
-  __gCrWeb['setWebViewScrollViewIsDragging'] = function(state) {
-    webViewScrollViewIsDragging_ = state;
-  };
-  var originalWindowScrollTo = window.scrollTo;
-  window.scrollTo = function(x, y) {
-    if (webViewScrollViewIsDragging_)
-      return;
-    originalWindowScrollTo(x, y);
-  };
-
-  window.addEventListener('hashchange', function(evt) {
-    invokeOnHost_({'command': 'window.hashchange'});
-  });
-
   // Flush the message queue.
   if (__gCrWeb.message) {
     __gCrWeb.message.invokeQueues();
   }
 
-  // Capture form submit actions.
-  document.addEventListener('submit', function(evt) {
-    var action;
-    if (evt['defaultPrevented'])
-      return;
-    action = evt.target.getAttribute('action');
-    // Default action is to re-submit to same page.
-    if (!action)
-      action = document.location.href;
-    invokeOnHost_({
-             'command': 'document.submit',
-            'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement),
-                'href': __gCrWeb['getFullyQualifiedURL'](action)
-    });
-  }, false);
-
-  addFormEventListeners_();
-
 }());  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/form.js b/ios/web/web_state/js/resources/form.js
new file mode 100644
index 0000000..c18d3da
--- /dev/null
+++ b/ios/web/web_state/js/resources/form.js
@@ -0,0 +1,89 @@
+// 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.
+
+/**
+ * @fileoverview Adds listeners that are used to handle forms, enabling autofill
+ * and the replacement method to dismiss the keyboard needed because of the
+ * Autofill keyboard accessory.
+ */
+
+goog.provide('__crWeb.form');
+
+goog.require('__crWeb.message');
+
+/** Beginning of anonymous object */
+(function() {
+
+  /**
+   * Focus and input events for form elements are messaged to the main
+   * application for broadcast to CRWWebControllerObservers.
+   * This is done with a single event handler for each type being added to the
+   * main document element which checks the source element of the event; this
+   * is much easier to manage than adding handlers to individual elements.
+   * @private
+   */
+  var formActivity_ = function(evt) {
+    var srcElement = evt.srcElement;
+    var fieldName = srcElement.name || '';
+    var value = srcElement.value || '';
+
+    var msg = {
+      'command': 'form.activity',
+      'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement.form),
+      'fieldName': fieldName,
+      'type': evt.type,
+      'value': value
+    };
+    __gCrWeb.message.invokeOnHost(msg);
+  };
+
+  /**
+   * Focus events performed on the 'capture' phase otherwise they are often
+   * not received.
+   */
+  document.addEventListener('focus', formActivity_, true);
+  document.addEventListener('blur', formActivity_, true);
+  document.addEventListener('change', formActivity_, true);
+
+  /**
+   * Text input is watched at the bubbling phase as this seems adequate in
+   * practice and it is less obtrusive to page scripts than capture phase.
+   */
+  document.addEventListener('input', formActivity_, false);
+  document.addEventListener('keyup', formActivity_, false);
+
+  /**
+   * Capture form submit actions.
+   */
+  document.addEventListener('submit', function(evt) {
+    var action;
+    if (evt['defaultPrevented'])
+      return;
+    action = evt.target.getAttribute('action');
+    // Default action is to re-submit to same page.
+    if (!action) {
+      action = document.location.href;
+    }
+    __gCrWeb.message.invokeOnHost({
+             'command': 'document.submit',
+            'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement),
+                'href': getFullyQualifiedUrl_(action)
+    });
+  }, false);
+
+  /** @private */
+  var getFullyQualifiedUrl_ = function(originalURL) {
+    // A dummy anchor (never added to the document) is used to obtain the
+    // fully-qualified URL of |originalURL|.
+    var anchor = document.createElement('a');
+    anchor.href = originalURL;
+    return anchor.href;
+  };
+
+  /** Flush the message queue. */
+  if (__gCrWeb.message) {
+    __gCrWeb.message.invokeQueues();
+  }
+
+}());  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/navigation.js b/ios/web/web_state/js/resources/navigation.js
new file mode 100644
index 0000000..4e299d5
--- /dev/null
+++ b/ios/web/web_state/js/resources/navigation.js
@@ -0,0 +1,128 @@
+// 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.
+
+/**
+ * @fileoverview Navigation related APIs.
+ */
+
+goog.provide('__crWeb.navigation');
+
+goog.require('__crWeb.message');
+
+/** Beginning of anonymouse object */
+(function() {
+
+  /**
+   * A popstate event needs to be fired anytime the active history entry
+   * changes without an associated document change. Either via back, forward, go
+   * navigation or by loading the URL, clicking on a link, etc.
+   */
+  __gCrWeb['dispatchPopstateEvent'] = function(stateObject) {
+    var popstateEvent = window.document.createEvent('HTMLEvents');
+    popstateEvent.initEvent('popstate', true, false);
+    if (stateObject)
+      popstateEvent.state = JSON.parse(stateObject);
+
+    // setTimeout() is used in order to return immediately. Otherwise the
+    // dispatchEvent call waits for all event handlers to return, which could
+    // cause a ReentryGuard failure.
+    window.setTimeout(function() {
+      window.dispatchEvent(popstateEvent);
+    }, 0);
+  };
+
+  /**
+   * A hashchange event needs to be fired after a same-document history
+   * navigation between two URLs that are equivalent except for their fragments.
+   */
+  __gCrWeb['dispatchHashchangeEvent'] = function(oldURL, newURL) {
+    var hashchangeEvent = window.document.createEvent('HTMLEvents');
+    hashchangeEvent.initEvent('hashchange', true, false);
+    if (oldURL)
+      hashchangeEvent.oldURL = oldURL;
+    if (newURL)
+      hashchangeEvent.newURL = newURL
+
+    // setTimeout() is used in order to return immediately. Otherwise the
+    // dispatchEvent call waits for all event handlers to return, which could
+    // cause a ReentryGuard failure.
+    window.setTimeout(function() {
+      window.dispatchEvent(hashchangeEvent);
+    }, 0);
+  };
+
+  /**
+   * Keep the original pushState() and replaceState() methods. It's needed to
+   * update the web view's URL and window.history.state property during history
+   * navigations that don't cause a page load.
+   * @private
+   */
+  var originalWindowHistoryPushState = window.history.pushState;
+  var originalWindowHistoryReplaceState = window.history.replaceState;
+
+  __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
+    originalWindowHistoryReplaceState.call(history, stateObject, '', url);
+  };
+
+  /**
+   * Intercept window.history methods to call back/forward natively.
+   */
+  window.history.back = function() {
+    __gCrWeb.message.invokeOnHost({'command': 'window.history.back'});
+  };
+
+  window.history.forward = function() {
+    __gCrWeb.message.invokeOnHost({'command': 'window.history.forward'});
+  };
+
+  window.history.go = function(delta) {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.go', 'value': delta | 0});
+  };
+
+  window.history.pushState = function(stateObject, pageTitle, pageUrl) {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.willChangeState'});
+    // Calling stringify() on undefined causes a JSON parse error.
+    var serializedState =
+        typeof(stateObject) == 'undefined' ? '' :
+            __gCrWeb.common.JSONStringify(stateObject);
+    pageUrl = pageUrl || window.location.href;
+    originalWindowHistoryPushState.call(history, stateObject,
+                                        pageTitle, pageUrl);
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.didPushState',
+         'stateObject': serializedState,
+         'baseUrl': document.baseURI,
+         'pageUrl': pageUrl.toString()});
+  };
+
+  window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.willChangeState'});
+
+    // Calling stringify() on undefined causes a JSON parse error.
+    var serializedState =
+        typeof(stateObject) == 'undefined' ? '' :
+            __gCrWeb.common.JSONStringify(stateObject);
+    pageUrl = pageUrl || window.location.href;
+    originalWindowHistoryReplaceState.call(history, stateObject,
+                                           pageTitle, pageUrl);
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.didReplaceState',
+         'stateObject': serializedState,
+         'baseUrl': document.baseURI,
+         'pageUrl': pageUrl.toString()});
+  };
+
+  window.addEventListener('hashchange', function(evt) {
+    __gCrWeb.message.invokeOnHost({'command': 'window.hashchange'});
+  });
+
+  /** Flush the message queue. */
+  if (__gCrWeb.message) {
+    __gCrWeb.message.invokeQueues();
+  }
+
+}());  // End of anonymouse object
diff --git a/ios/web/web_state/js/resources/scroll_workaround.js b/ios/web/web_state/js/resources/scroll_workaround.js
new file mode 100644
index 0000000..a4ef6d5
--- /dev/null
+++ b/ios/web/web_state/js/resources/scroll_workaround.js
@@ -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.
+
+/**
+ * @fileoverview APIs used for the scroll workaround. See crbug.com/554257.
+ */
+
+goog.provide('__crWeb.scrollWorkaround');
+
+/** Beginning of anonymous object */
+(function() {
+
+  /** @private */
+  var webViewScrollViewIsDragging_ = false;
+
+  /**
+   * Tracks whether user is in the middle of scrolling/dragging. If user is
+   * scrolling, ignore window.scrollTo() until user stops scrolling.
+   */
+  __gCrWeb['setWebViewScrollViewIsDragging'] = function(state) {
+    webViewScrollViewIsDragging_ = state;
+  };
+
+  /** @private */
+  var originalWindowScrollTo_ = window.scrollTo;
+
+  /**
+   * Wraps the original window.scrollTo() to suppress it as long as
+   * webViewScrollViewIsDragging is true.
+   */
+  window.scrollTo = function(x, y) {
+    if (webViewScrollViewIsDragging_)
+      return;
+    originalWindowScrollTo_(x, y);
+  };
+
+}())  // End of anonymouse object
diff --git a/ios/web/web_state/js/resources/web_bundle.js b/ios/web/web_state/js/resources/web_bundle.js
index 74cae382..867643d 100644
--- a/ios/web/web_state/js/resources/web_bundle.js
+++ b/ios/web/web_state/js/resources/web_bundle.js
@@ -9,3 +9,6 @@
 goog.require('__crWeb.contextMenu');
 goog.require('__crWeb.core');
 goog.require('__crWeb.dialogOverrides');
+goog.require('__crWeb.form');
+goog.require('__crWeb.navigation');
+goog.require('__crWeb.scrollWorkaround');
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 1ec009d..4d87b2c3 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2595,7 +2595,7 @@
   std::vector<std::string> federations;
   for (const auto& federation_value : *federations_value) {
     std::string federation;
-    if (!federation_value->GetAsString(&federation)) {
+    if (!federation_value.GetAsString(&federation)) {
       DLOG(WARNING) << "JS message parameter 'federations' contains wrong type";
       return NO;
     }
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index c17c8be6..23cfd133 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -128,7 +128,7 @@
       sizer->AddInt();
       const base::ListValue* list = static_cast<const base::ListValue*>(value);
       for (const auto& entry : *list) {
-        GetValueSize(sizer, entry.get(), recursion + 1);
+        GetValueSize(sizer, &entry, recursion + 1);
       }
       break;
     }
@@ -198,7 +198,7 @@
       const base::ListValue* list = static_cast<const base::ListValue*>(value);
       WriteParam(m, static_cast<int>(list->GetSize()));
       for (const auto& entry : *list) {
-        WriteValue(m, entry.get(), recursion + 1);
+        WriteValue(m, &entry, recursion + 1);
       }
       break;
     }
diff --git a/media/audio/audio_manager.cc b/media/audio/audio_manager.cc
index 4b98e36..9fb92703 100644
--- a/media/audio/audio_manager.cc
+++ b/media/audio/audio_manager.cc
@@ -254,21 +254,19 @@
     LOG(WARNING) << "Multiple instances of AudioManager detected";
   }
 
-#if defined(OS_MACOSX)
-  // If we are on Mac, tasks after this point are not executed, hence this is
-  // the only chance to delete the audio manager (which on Mac lives on the
-  // main browser thread instead of a dedicated audio thread). If we don't
-  // delete here, the CoreAudio thread can keep providing callbacks, which
-  // uses a state that is destroyed in ~BrowserMainLoop().
-  // See http://crbug.com/623703 for more details.
-  DCHECK(instance->GetTaskRunner()->BelongsToCurrentThread());
-  delete instance;
-#else
-  // AudioManager must be destroyed on the audio thread.
-  if (!instance->GetTaskRunner()->DeleteSoon(FROM_HERE, instance)) {
-    LOG(WARNING) << "Failed to delete AudioManager instance.";
+  // The deleter runs on the main thread, and AudioManager must be destroyed on
+  // the audio thread. If the audio thread is the same as the main one, tasks
+  // after this point are not executed, hence this is the only chance to delete
+  // AudioManager. See http://crbug.com/623703 for more details.
+  if (instance->GetTaskRunner()->BelongsToCurrentThread()) {
+    delete instance;
+    return;
   }
-#endif
+
+  // AudioManager must be destroyed on the audio thread. See
+  // http://crbug.com/705455 for an existing AudioManager lifetime issue.
+  if (!instance->GetTaskRunner()->DeleteSoon(FROM_HERE, instance))
+    LOG(WARNING) << "Failed to delete AudioManager instance.";
 }
 
 // Forward declaration of the platform specific AudioManager factory function.
diff --git a/media/audio/audio_system.h b/media/audio/audio_system.h
index 8a87e5a..932151e 100644
--- a/media/audio/audio_system.h
+++ b/media/audio/audio_system.h
@@ -23,13 +23,18 @@
 class MEDIA_EXPORT AudioSystem {
  public:
   // Replies are asynchronously sent from audio system thread to the thread the
-  // call is issued on. Attention! Since audio system thread may outlive all the
-  // others, callbacks must always be bound to weak pointers!
+  // call is issued on. Attention! Audio system thread may outlive the client
+  // objects; bind callbacks with care.
   using OnAudioParamsCallback = base::Callback<void(const AudioParameters&)>;
   using OnBoolCallback = base::Callback<void(bool)>;
   using OnDeviceDescriptionsCallback =
       base::Callback<void(AudioDeviceDescriptions)>;
+  using OnDeviceIdCallback = base::Callback<void(const std::string&)>;
+  using OnInputDeviceInfoCallback = base::Callback<
+      void(const AudioParameters&, const AudioParameters&, const std::string&)>;
 
+  // Must not be called on audio system thread if it differs from the one
+  // AudioSystem is destroyed on. See http://crbug.com/705455.
   static AudioSystem* Get();
 
   virtual ~AudioSystem();
@@ -61,13 +66,22 @@
   // Replies with device descriptions of input audio devices if |for_input| is
   // true, and of output audio devices otherwise.
   virtual void GetDeviceDescriptions(
-      OnDeviceDescriptionsCallback on_descriptions_cp,
+      OnDeviceDescriptionsCallback on_descriptions_cb,
       bool for_input) = 0;
 
-  virtual base::SingleThreadTaskRunner* GetTaskRunner() const = 0;
+  // Replies with an empty string if there is no associated output device found.
+  virtual void GetAssociatedOutputDeviceID(
+      const std::string& input_device_id,
+      OnDeviceIdCallback on_device_id_cb) = 0;
 
-  // Must not be used for anything but stream creation.
-  virtual AudioManager* GetAudioManager() const = 0;
+  // Replies with audio parameters for the specified input device and audio
+  // parameters and device ID of the associated output device, if any (otherwise
+  // it's AudioParameters() and an empty string).
+  virtual void GetInputDeviceInfo(
+      const std::string& input_device_id,
+      OnInputDeviceInfoCallback on_input_device_info_cb) = 0;
+
+  virtual base::SingleThreadTaskRunner* GetTaskRunner() const = 0;
 
  protected:
   // Sets the global AudioSystem pointer to the specified non-null value.
diff --git a/media/audio/audio_system_impl.cc b/media/audio/audio_system_impl.cc
index 7504744..9d009aa 100644
--- a/media/audio/audio_system_impl.cc
+++ b/media/audio/audio_system_impl.cc
@@ -9,6 +9,7 @@
 #include "base/task_runner_util.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_manager.h"
+#include "media/base/bind_to_current_loop.h"
 
 // Using base::Unretained for |audio_manager_| is safe since it is deleted after
 // its task runner, and AudioSystemImpl is deleted on the UI thread after the IO
@@ -57,6 +58,23 @@
   return descriptions;
 }
 
+void GetInputDeviceInfoOnDeviceThread(
+    AudioManager* audio_manager,
+    const std::string& input_device_id,
+    AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb) {
+  DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
+  const std::string associated_output_device_id =
+      audio_manager->GetAssociatedOutputDeviceID(input_device_id);
+
+  on_input_device_info_cb.Run(
+      GetInputParametersOnDeviceThread(audio_manager, input_device_id),
+      associated_output_device_id.empty()
+          ? AudioParameters()
+          : GetOutputParametersOnDeviceThread(audio_manager,
+                                              associated_output_device_id),
+      associated_output_device_id);
+}
+
 }  // namespace
 
 AudioSystemImpl::AudioSystemImpl(AudioManager* audio_manager)
@@ -136,11 +154,11 @@
 }
 
 void AudioSystemImpl::GetDeviceDescriptions(
-    OnDeviceDescriptionsCallback on_descriptions_cp,
+    OnDeviceDescriptionsCallback on_descriptions_cb,
     bool for_input) {
   if (GetTaskRunner()->BelongsToCurrentThread()) {
     GetTaskRunner()->PostTask(
-        FROM_HERE, base::Bind(on_descriptions_cp,
+        FROM_HERE, base::Bind(on_descriptions_cb,
                               base::Passed(GetDeviceDescriptionsOnDeviceThread(
                                   audio_manager_, for_input))));
     return;
@@ -150,11 +168,40 @@
       GetTaskRunner(), FROM_HERE,
       base::Bind(&GetDeviceDescriptionsOnDeviceThread,
                  base::Unretained(audio_manager_), for_input),
-      std::move(on_descriptions_cp));
+      std::move(on_descriptions_cb));
 }
 
-AudioManager* AudioSystemImpl::GetAudioManager() const {
-  return audio_manager_;
+void AudioSystemImpl::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id,
+    OnDeviceIdCallback on_device_id_cb) {
+  if (GetTaskRunner()->BelongsToCurrentThread()) {
+    GetTaskRunner()->PostTask(
+        FROM_HERE,
+        base::Bind(on_device_id_cb, audio_manager_->GetAssociatedOutputDeviceID(
+                                        input_device_id)));
+    return;
+  }
+  base::PostTaskAndReplyWithResult(
+      GetTaskRunner(), FROM_HERE,
+      base::Bind(&AudioManager::GetAssociatedOutputDeviceID,
+                 base::Unretained(audio_manager_), input_device_id),
+      std::move(on_device_id_cb));
+}
+
+void AudioSystemImpl::GetInputDeviceInfo(
+    const std::string& input_device_id,
+    OnInputDeviceInfoCallback on_input_device_info_cb) {
+  // No need to bind |on_input_device_info_cb| to the current loop if we are on
+  // the audio thread. However, the client still expect to receive the reply
+  // asynchronously, so we always post GetInputDeviceInfoOnDeviceThread(), which
+  // will syncronously call the (bound to current loop or not) callback.
+  GetTaskRunner()->PostTask(
+      FROM_HERE, base::Bind(&GetInputDeviceInfoOnDeviceThread,
+                            base::Unretained(audio_manager_), input_device_id,
+                            GetTaskRunner()->BelongsToCurrentThread()
+                                ? std::move(on_input_device_info_cb)
+                                : media::BindToCurrentLoop(
+                                      std::move(on_input_device_info_cb))));
 }
 
 base::SingleThreadTaskRunner* AudioSystemImpl::GetTaskRunner() const {
diff --git a/media/audio/audio_system_impl.h b/media/audio/audio_system_impl.h
index d98bc25..10884d08 100644
--- a/media/audio/audio_system_impl.h
+++ b/media/audio/audio_system_impl.h
@@ -36,9 +36,14 @@
   void GetDeviceDescriptions(OnDeviceDescriptionsCallback on_descriptions_cp,
                              bool for_input) override;
 
-  base::SingleThreadTaskRunner* GetTaskRunner() const override;
+  void GetAssociatedOutputDeviceID(const std::string& input_device_id,
+                                   OnDeviceIdCallback on_device_id_cb) override;
 
-  AudioManager* GetAudioManager() const override;
+  void GetInputDeviceInfo(
+      const std::string& input_device_id,
+      OnInputDeviceInfoCallback on_input_device_info_cb) override;
+
+  base::SingleThreadTaskRunner* GetTaskRunner() const override;
 
  protected:
   AudioSystemImpl(AudioManager* audio_manager);
diff --git a/media/audio/audio_system_impl_unittest.cc b/media/audio/audio_system_impl_unittest.cc
index 76f036b..618bf929 100644
--- a/media/audio/audio_system_impl_unittest.cc
+++ b/media/audio/audio_system_impl_unittest.cc
@@ -111,6 +111,21 @@
     DeviceDescriptionsReceived();
   }
 
+  void OnInputDeviceInfo(const AudioParameters& expected_input,
+                         const AudioParameters& expected_associated_output,
+                         const std::string& expected_associated_device_id,
+                         const AudioParameters& input,
+                         const AudioParameters& associated_output,
+                         const std::string& associated_device_id) {
+    EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+    EXPECT_EQ(expected_input.AsHumanReadableString(),
+              input.AsHumanReadableString());
+    EXPECT_EQ(expected_associated_output.AsHumanReadableString(),
+              associated_output.AsHumanReadableString());
+    EXPECT_EQ(expected_associated_device_id, associated_device_id);
+    InputDeviceInfoReceived();
+  }
+
   void WaitForCallback() {
     if (!use_audio_thread_) {
       base::RunLoop().RunUntilIdle();
@@ -131,6 +146,8 @@
   MOCK_METHOD1(HasInputDevicesCallback, void(bool));
   MOCK_METHOD1(HasOutputDevicesCallback, void(bool));
   MOCK_METHOD0(DeviceDescriptionsReceived, void(void));
+  MOCK_METHOD1(AssociatedOutputDeviceIDReceived, void(const std::string&));
+  MOCK_METHOD0(InputDeviceInfoReceived, void(void));
 
  protected:
   base::MessageLoop message_loop_;
@@ -165,7 +182,7 @@
   WaitForCallback();
 }
 
-TEST_P(AudioSystemImplTest, GetStreamParameters) {
+TEST_P(AudioSystemImplTest, GetOutputStreamParameters) {
   EXPECT_CALL(*this, AudioParametersReceived());
   audio_system_->GetOutputStreamParameters(
       kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnAudioParams,
@@ -289,6 +306,48 @@
   WaitForCallback();
 }
 
+TEST_P(AudioSystemImplTest, GetAssociatedOutputDeviceID) {
+  const std::string associated_id("associated_id");
+  audio_manager_->SetAssociatedOutputDeviceIDCallback(
+      base::Bind([](const std::string& result,
+                    const std::string&) -> std::string { return result; },
+                 associated_id));
+
+  EXPECT_CALL(*this, AssociatedOutputDeviceIDReceived(associated_id));
+
+  audio_system_->GetAssociatedOutputDeviceID(
+      std::string(),
+      base::Bind(&AudioSystemImplTest::AssociatedOutputDeviceIDReceived,
+                 base::Unretained(this)));
+  WaitForCallback();
+}
+
+TEST_P(AudioSystemImplTest, GetInputDeviceInfoNoAssociation) {
+  EXPECT_CALL(*this, InputDeviceInfoReceived());
+
+  audio_system_->GetInputDeviceInfo(
+      kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnInputDeviceInfo,
+                                      base::Unretained(this), input_params_,
+                                      AudioParameters(), std::string()));
+  WaitForCallback();
+}
+
+TEST_P(AudioSystemImplTest, GetInputDeviceInfoWithAssociation) {
+  EXPECT_CALL(*this, InputDeviceInfoReceived());
+
+  const std::string associated_id("associated_id");
+  audio_manager_->SetAssociatedOutputDeviceIDCallback(
+      base::Bind([](const std::string& result,
+                    const std::string&) -> std::string { return result; },
+                 associated_id));
+
+  audio_system_->GetInputDeviceInfo(
+      kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnInputDeviceInfo,
+                                      base::Unretained(this), input_params_,
+                                      output_params_, associated_id));
+  WaitForCallback();
+}
+
 INSTANTIATE_TEST_CASE_P(, AudioSystemImplTest, testing::Values(false, true));
 
 }  // namespace media
diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc
index b89cb5e..b6c09e0 100644
--- a/media/audio/mock_audio_manager.cc
+++ b/media/audio/mock_audio_manager.cc
@@ -115,7 +115,9 @@
 std::string MockAudioManager::GetAssociatedOutputDeviceID(
     const std::string& input_device_id) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
-  return std::string();
+  return get_associated_output_device_id_cb_.is_null()
+             ? std::string()
+             : get_associated_output_device_id_cb_.Run(input_device_id);
 }
 
 std::unique_ptr<AudioLog> MockAudioManager::CreateAudioLog(
@@ -167,4 +169,9 @@
   get_output_device_descriptions_cb_ = std::move(callback);
 }
 
+void MockAudioManager::SetAssociatedOutputDeviceIDCallback(
+    GetAssociatedOutputDeviceIDCallback callback) {
+  get_associated_output_device_id_cb_ = std::move(callback);
+}
+
 }  // namespace media.
diff --git a/media/audio/mock_audio_manager.h b/media/audio/mock_audio_manager.h
index 69e0d14..781729a5 100644
--- a/media/audio/mock_audio_manager.h
+++ b/media/audio/mock_audio_manager.h
@@ -26,6 +26,8 @@
   using UniquePtr = std::unique_ptr<MockAudioManager, Deleter>;
   using GetDeviceDescriptionsCallback =
       base::RepeatingCallback<void(AudioDeviceDescriptions*)>;
+  using GetAssociatedOutputDeviceIDCallback =
+      base::RepeatingCallback<std::string(const std::string&)>;
 
   explicit MockAudioManager(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
@@ -90,6 +92,8 @@
       GetDeviceDescriptionsCallback callback);
   void SetOutputDeviceDescriptionsCallback(
       GetDeviceDescriptionsCallback callback);
+  void SetAssociatedOutputDeviceIDCallback(
+      GetAssociatedOutputDeviceIDCallback callback);
 
  protected:
   ~MockAudioManager() override;
@@ -104,6 +108,7 @@
   bool has_output_devices_ = true;
   GetDeviceDescriptionsCallback get_input_device_descriptions_cb_;
   GetDeviceDescriptionsCallback get_output_device_descriptions_cb_;
+  GetAssociatedOutputDeviceIDCallback get_associated_output_device_id_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
 };
diff --git a/media/base/audio_renderer.h b/media/base/audio_renderer.h
index 48cf7b7..0bd368d 100644
--- a/media/base/audio_renderer.h
+++ b/media/base/audio_renderer.h
@@ -32,6 +32,11 @@
   //
   // |cdm_context| can be used to handle encrypted streams. May be null if the
   // stream is not encrypted.
+  //
+  // AudioRenderer may be reinitialized for playback of a different demuxer
+  // stream by calling Initialize again when the renderer is in a flushed
+  // state (i.e. after Flush call, but before StartPlaying). This is used for
+  // media track switching.
   virtual void Initialize(DemuxerStream* stream,
                           CdmContext* cdm_context,
                           RendererClient* client,
diff --git a/media/base/fake_audio_renderer_sink.cc b/media/base/fake_audio_renderer_sink.cc
index 48e448d..2468491 100644
--- a/media/base/fake_audio_renderer_sink.cc
+++ b/media/base/fake_audio_renderer_sink.cc
@@ -32,7 +32,7 @@
 
 void FakeAudioRendererSink::Initialize(const AudioParameters& params,
                                        RenderCallback* callback) {
-  DCHECK_EQ(state_, kUninitialized);
+  DCHECK(state_ == kUninitialized || state_ == kStopped);
   DCHECK(!callback_);
   DCHECK(callback);
 
diff --git a/media/base/video_renderer.h b/media/base/video_renderer.h
index 168b7e8..7b6ba90 100644
--- a/media/base/video_renderer.h
+++ b/media/base/video_renderer.h
@@ -33,6 +33,11 @@
   //
   // |wall_clock_time_cb| is used to convert media timestamps into wallclock
   // timestamps.
+  //
+  // VideoRenderer may be reinitialized for playback of a different demuxer
+  // stream by calling Initialize again when the renderer is in a flushed
+  // state (i.e. after Flush call, but before StartPlayingFrom). This is used
+  // for media track switching.
   virtual void Initialize(DemuxerStream* stream,
                           CdmContext* cdm_context,
                           RendererClient* client,
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index be11a949..366ada9 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1025,20 +1025,25 @@
   DCHECK(demuxer_.get());
   DCHECK(!chunk_demuxer_);
 
-  // Report the media track information to blink.
+  // Report the media track information to blink. Only the first audio track and
+  // the first video track are enabled by default to match blink logic.
+  bool is_first_audio_track = true;
+  bool is_first_video_track = true;
   for (const auto& track : tracks->tracks()) {
     if (track->type() == MediaTrack::Audio) {
       client_->AddAudioTrack(blink::WebString::FromUTF8(track->id()),
                              blink::WebMediaPlayerClient::kAudioTrackKindMain,
                              blink::WebString::FromUTF8(track->label()),
                              blink::WebString::FromUTF8(track->language()),
-                             /*enabled*/ true);
+                             is_first_audio_track);
+      is_first_audio_track = false;
     } else if (track->type() == MediaTrack::Video) {
       client_->AddVideoTrack(blink::WebString::FromUTF8(track->id()),
                              blink::WebMediaPlayerClient::kVideoTrackKindMain,
                              blink::WebString::FromUTF8(track->label()),
                              blink::WebString::FromUTF8(track->language()),
-                             /*selected*/ true);
+                             is_first_video_track);
+      is_first_video_track = false;
     } else {
       // Text tracks are not supported through this code path yet.
       NOTREACHED();
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index d55b2de..9fb533e 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -715,6 +715,13 @@
     ChunkDemuxerStream* stream = track_id_to_demux_stream_map_[id];
     DCHECK(stream);
     DCHECK_EQ(DemuxerStream::AUDIO, stream->type());
+    // TODO(servolk): Remove after multiple enabled audio tracks are supported
+    // by the media::RendererImpl.
+    if (!enabled_streams.empty()) {
+      MEDIA_LOG(INFO, media_log_)
+          << "Only one enabled audio track is supported, ignoring track " << id;
+      continue;
+    }
     enabled_streams.insert(stream);
   }
 
@@ -1227,6 +1234,7 @@
          track_id_to_demux_stream_map_.end());
   track_id_to_demux_stream_map_[media_track_id] = stream.get();
   id_to_streams_map_[source_id].push_back(stream.get());
+  stream->set_enabled(owning_vector->empty(), base::TimeDelta());
   owning_vector->push_back(std::move(stream));
   return owning_vector->back().get();
 }
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index c686aa9..5f37c6e 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -1338,6 +1338,14 @@
       track_label = streams_[i]->GetMetadata("title");
     }
 
+    if (codec_type == AVMEDIA_TYPE_AUDIO) {
+      streams_[i]->set_enabled(detected_audio_track_count == 1,
+                               base::TimeDelta());
+    } else if (codec_type == AVMEDIA_TYPE_VIDEO) {
+      streams_[i]->set_enabled(detected_video_track_count == 1,
+                               base::TimeDelta());
+    }
+
     if ((codec_type == AVMEDIA_TYPE_AUDIO &&
          media_tracks->getAudioConfig(track_id).IsValidConfig()) ||
         (codec_type == AVMEDIA_TYPE_VIDEO &&
@@ -1656,6 +1664,13 @@
     FFmpegDemuxerStream* stream = track_id_to_demux_stream_map_[id];
     DCHECK(stream);
     DCHECK_EQ(DemuxerStream::AUDIO, stream->type());
+    // TODO(servolk): Remove after multiple enabled audio tracks are supported
+    // by the media::RendererImpl.
+    if (!enabled_streams.empty()) {
+      MEDIA_LOG(INFO, media_log_)
+          << "Only one enabled audio track is supported, ignoring track " << id;
+      continue;
+    }
     enabled_streams.insert(stream);
   }
 
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index 596603b..523d5d6d 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -36,13 +36,11 @@
 AudioRendererImpl::AudioRendererImpl(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     media::AudioRendererSink* sink,
-    ScopedVector<AudioDecoder> decoders,
+    const CreateAudioDecodersCB& create_audio_decoders_cb,
     const scoped_refptr<MediaLog>& media_log)
     : task_runner_(task_runner),
       expecting_config_changes_(false),
       sink_(sink),
-      audio_buffer_stream_(
-          new AudioBufferStream(task_runner, std::move(decoders), media_log)),
       media_log_(media_log),
       client_(nullptr),
       tick_clock_(new base::DefaultTickClock()),
@@ -51,6 +49,7 @@
       last_decoded_channel_layout_(CHANNEL_LAYOUT_NONE),
       playback_rate_(0.0),
       state_(kUninitialized),
+      create_audio_decoders_cb_(create_audio_decoders_cb),
       buffering_state_(BUFFERING_HAVE_NOTHING),
       rendering_(false),
       sink_playing_(false),
@@ -59,9 +58,7 @@
       rendered_end_of_stream_(false),
       is_suspending_(false),
       weak_factory_(this) {
-  audio_buffer_stream_->set_config_change_observer(base::Bind(
-      &AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
-
+  DCHECK(create_audio_decoders_cb_);
   // Tests may not have a power monitor.
   base::PowerMonitor* monitor = base::PowerMonitor::Get();
   if (!monitor)
@@ -340,9 +337,16 @@
   DCHECK(stream);
   DCHECK_EQ(stream->type(), DemuxerStream::AUDIO);
   DCHECK(!init_cb.is_null());
-  DCHECK_EQ(kUninitialized, state_);
+  DCHECK(state_ == kUninitialized || state_ == kFlushed);
   DCHECK(sink_.get());
 
+  // If we are re-initializing playback (e.g. switching media tracks), stop the
+  // sink first.
+  if (state_ == kFlushed) {
+    sink_->Stop();
+    audio_clock_.reset();
+  }
+
   // Trying to track down AudioClock crash, http://crbug.com/674856.
   // AudioRenderImpl should only be initialized once to avoid destroying
   // AudioClock while the audio thread is still using it.
@@ -351,6 +355,12 @@
   state_ = kInitializing;
   client_ = client;
 
+  audio_buffer_stream_ = base::MakeUnique<AudioBufferStream>(
+      task_runner_, create_audio_decoders_cb_.Run(), media_log_);
+
+  audio_buffer_stream_->set_config_change_observer(base::Bind(
+      &AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
+
   // Always post |init_cb_| because |this| could be destroyed if initialization
   // failed.
   init_cb_ = BindToCurrentLoop(init_cb);
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h
index f7915d31..93d1989 100644
--- a/media/renderers/audio_renderer_impl.h
+++ b/media/renderers/audio_renderer_impl.h
@@ -36,6 +36,7 @@
 #include "media/base/time_source.h"
 #include "media/filters/audio_renderer_algorithm.h"
 #include "media/filters/decoder_stream.h"
+#include "media/renderers/default_renderer_factory.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -62,7 +63,7 @@
   AudioRendererImpl(
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
       AudioRendererSink* sink,
-      ScopedVector<AudioDecoder> decoders,
+      const CreateAudioDecodersCB& create_audio_decoders_cb,
       const scoped_refptr<MediaLog>& media_log);
   ~AudioRendererImpl() override;
 
@@ -96,16 +97,18 @@
   // Important detail: being in kPlaying doesn't imply that audio is being
   // rendered. Rather, it means that the renderer is ready to go. The actual
   // rendering of audio is controlled via Start/StopRendering().
+  // Audio renderer can be reinitialized completely by calling Initialize again
+  // when it is in a kFlushed state.
   //
   //   kUninitialized
-  //         | Initialize()
-  //         |
-  //         V
-  //    kInitializing
-  //         | Decoders initialized
-  //         |
-  //         V            Decoders reset
-  //      kFlushed <------------------ kFlushing
+  //  +----> | Initialize()
+  //  |      |
+  //  |      V
+  //  | kInitializing
+  //  |      | Decoders initialized
+  //  |      |
+  //  |      V            Decoders reset
+  //  +-  kFlushed <------------------ kFlushing
   //         | StartPlaying()             ^
   //         |                            |
   //         |                            | Flush()
@@ -253,6 +256,10 @@
   // Simple state tracking variable.
   State state_;
 
+  // TODO(servolk): Consider using DecoderFactory here instead of the
+  // CreateAudioDecodersCB.
+  CreateAudioDecodersCB create_audio_decoders_cb_;
+
   BufferingState buffering_state_;
 
   // Keep track of whether or not the sink is playing and whether we should be
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index b677889..6fad71c 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -66,6 +66,25 @@
 
 class AudioRendererImplTest : public ::testing::Test, public RendererClient {
  public:
+  ScopedVector<AudioDecoder> CreateAudioDecoderForTest() {
+    MockAudioDecoder* decoder = new MockAudioDecoder();
+    if (!enter_pending_decoder_init_) {
+      EXPECT_CALL(*decoder, Initialize(_, _, _, _))
+          .WillOnce(DoAll(SaveArg<3>(&output_cb_),
+                          RunCallback<2>(expected_init_result_)));
+    } else {
+      EXPECT_CALL(*decoder, Initialize(_, _, _, _))
+          .WillOnce(EnterPendingDecoderInitStateAction(this));
+    }
+    EXPECT_CALL(*decoder, Decode(_, _))
+        .WillRepeatedly(Invoke(this, &AudioRendererImplTest::DecodeDecoder));
+    EXPECT_CALL(*decoder, Reset(_))
+        .WillRepeatedly(Invoke(this, &AudioRendererImplTest::ResetDecoder));
+    ScopedVector<AudioDecoder> decoders;
+    decoders.push_back(decoder);
+    return decoders;
+  }
+
   // Give the decoder some non-garbage media properties.
   AudioRendererImplTest()
       : hardware_params_(AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -76,14 +95,14 @@
         sink_(new FakeAudioRendererSink(hardware_params_)),
         tick_clock_(new base::SimpleTestTickClock()),
         demuxer_stream_(DemuxerStream::AUDIO),
-        decoder_(new MockAudioDecoder()),
+        expected_init_result_(true),
+        enter_pending_decoder_init_(false),
         ended_(false) {
     AudioDecoderConfig audio_config(kCodec, kSampleFormat, kChannelLayout,
                                     kInputSamplesPerSecond, EmptyExtraData(),
                                     Unencrypted());
     demuxer_stream_.set_audio_decoder_config(audio_config);
 
-    ConfigureDecoder();
     ConfigureDemuxerStream(true);
 
     AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -91,11 +110,11 @@
                                kOutputSamplesPerSecond,
                                SampleFormatToBytesPerChannel(kSampleFormat) * 8,
                                512);
-    ScopedVector<AudioDecoder> decoders;
-    decoders.push_back(decoder_);
-    renderer_.reset(new AudioRendererImpl(message_loop_.task_runner(),
-                                          sink_.get(), std::move(decoders),
-                                          new MediaLog()));
+    renderer_.reset(new AudioRendererImpl(
+        message_loop_.task_runner(), sink_.get(),
+        base::Bind(&AudioRendererImplTest::CreateAudioDecoderForTest,
+                   base::Unretained(this)),
+        new MediaLog()));
     renderer_->tick_clock_.reset(tick_clock_);
     tick_clock_->Advance(base::TimeDelta::FromSeconds(1));
   }
@@ -104,14 +123,6 @@
     SCOPED_TRACE("~AudioRendererImplTest()");
   }
 
-  // Used to save callbacks and run them at a later time.
-  void ConfigureDecoder() {
-    EXPECT_CALL(*decoder_, Decode(_, _))
-        .WillRepeatedly(Invoke(this, &AudioRendererImplTest::DecodeDecoder));
-    EXPECT_CALL(*decoder_, Reset(_))
-        .WillRepeatedly(Invoke(this, &AudioRendererImplTest::ResetDecoder));
-  }
-
   // Mock out demuxer reads.
   void ConfigureDemuxerStream(bool supports_config_changes) {
     EXPECT_CALL(demuxer_stream_, Read(_))
@@ -126,13 +137,11 @@
   void ConfigureBasicRenderer(const AudioParameters& params) {
     hardware_params_ = params;
     sink_ = new FakeAudioRendererSink(hardware_params_);
-    decoder_ = new MockAudioDecoder();
-    ConfigureDecoder();
-    ScopedVector<AudioDecoder> decoders;
-    decoders.push_back(decoder_);
-    renderer_.reset(new AudioRendererImpl(message_loop_.task_runner(),
-                                          sink_.get(), std::move(decoders),
-                                          new MediaLog()));
+    renderer_.reset(new AudioRendererImpl(
+        message_loop_.task_runner(), sink_.get(),
+        base::Bind(&AudioRendererImplTest::CreateAudioDecoderForTest,
+                   base::Unretained(this)),
+        new MediaLog()));
     testing::Mock::VerifyAndClearExpectations(&demuxer_stream_);
     ConfigureDemuxerStream(false);
   }
@@ -142,22 +151,15 @@
                                      const AudioParameters& hardware_params) {
     hardware_params_ = hardware_params;
     sink_ = new FakeAudioRendererSink(hardware_params_);
-    decoder_ = new MockAudioDecoder();
-    ConfigureDecoder();
-    ScopedVector<AudioDecoder> decoders;
-    decoders.push_back(decoder_);
-    renderer_.reset(new AudioRendererImpl(message_loop_.task_runner(),
-                                          sink_.get(), std::move(decoders),
-                                          new MediaLog()));
+    renderer_.reset(new AudioRendererImpl(
+        message_loop_.task_runner(), sink_.get(),
+        base::Bind(&AudioRendererImplTest::CreateAudioDecoderForTest,
+                   base::Unretained(this)),
+        new MediaLog()));
     testing::Mock::VerifyAndClearExpectations(&demuxer_stream_);
     ConfigureDemuxerStream(true);
   }
 
-  void ExpectUnsupportedAudioDecoder() {
-    EXPECT_CALL(*decoder_, Initialize(_, _, _, _))
-        .WillOnce(DoAll(SaveArg<3>(&output_cb_), RunCallback<2>(false)));
-  }
-
   // RendererClient implementation.
   MOCK_METHOD1(OnError, void(PipelineStatus));
   void OnEnded() override {
@@ -173,16 +175,15 @@
   MOCK_METHOD1(OnVideoOpacityChange, void(bool));
   MOCK_METHOD1(OnDurationChange, void(base::TimeDelta));
 
-  void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) {
+  void InitializeRenderer(DemuxerStream* demuxer_stream,
+                          const PipelineStatusCB& pipeline_status_cb) {
     EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
     EXPECT_CALL(*this, OnVideoNaturalSizeChange(_)).Times(0);
     EXPECT_CALL(*this, OnVideoOpacityChange(_)).Times(0);
-    renderer_->Initialize(&demuxer_stream_, nullptr, this, pipeline_status_cb);
+    renderer_->Initialize(demuxer_stream, nullptr, this, pipeline_status_cb);
   }
 
   void Initialize() {
-    EXPECT_CALL(*decoder_, Initialize(_, _, _, _))
-        .WillOnce(DoAll(SaveArg<3>(&output_cb_), RunCallback<2>(true)));
     InitializeWithStatus(PIPELINE_OK);
 
     next_timestamp_.reset(new AudioTimestampHelper(kInputSamplesPerSecond));
@@ -192,7 +193,7 @@
     SCOPED_TRACE(base::StringPrintf("InitializeWithStatus(%d)", expected));
 
     WaitableMessageLoopEvent event;
-    InitializeRenderer(event.GetPipelineStatusCB());
+    InitializeRenderer(&demuxer_stream_, event.GetPipelineStatusCB());
     event.RunAndWaitForStatus(expected);
 
     // We should have no reads.
@@ -200,11 +201,8 @@
   }
 
   void InitializeAndDestroy() {
-    EXPECT_CALL(*decoder_, Initialize(_, _, _, _))
-        .WillOnce(RunCallback<2>(true));
-
     WaitableMessageLoopEvent event;
-    InitializeRenderer(event.GetPipelineStatusCB());
+    InitializeRenderer(&demuxer_stream_, event.GetPipelineStatusCB());
 
     // Destroy the |renderer_| before we let the MessageLoop run, this simulates
     // an interleaving in which we end up destroying the |renderer_| while the
@@ -214,11 +212,10 @@
   }
 
   void InitializeAndDestroyDuringDecoderInit() {
-    EXPECT_CALL(*decoder_, Initialize(_, _, _, _))
-        .WillOnce(EnterPendingDecoderInitStateAction(this));
+    enter_pending_decoder_init_ = true;
 
     WaitableMessageLoopEvent event;
-    InitializeRenderer(event.GetPipelineStatusCB());
+    InitializeRenderer(&demuxer_stream_, event.GetPipelineStatusCB());
     base::RunLoop().RunUntilIdle();
     DCHECK(!init_decoder_cb_.is_null());
 
@@ -457,7 +454,6 @@
   PipelineStatistics last_statistics_;
 
   MockDemuxerStream demuxer_stream_;
-  MockAudioDecoder* decoder_;
 
   // Used for satisfying reads.
   AudioDecoder::OutputCB output_cb_;
@@ -469,6 +465,8 @@
   base::Closure wait_for_pending_decode_cb_;
 
   AudioDecoder::InitCB init_decoder_cb_;
+  bool expected_init_result_;
+  bool enter_pending_decoder_init_;
   bool ended_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest);
@@ -479,10 +477,39 @@
 }
 
 TEST_F(AudioRendererImplTest, Initialize_DecoderInitFailure) {
-  ExpectUnsupportedAudioDecoder();
+  expected_init_result_ = false;
   InitializeWithStatus(DECODER_ERROR_NOT_SUPPORTED);
 }
 
+TEST_F(AudioRendererImplTest, ReinitializeForDifferentStream) {
+  // Initialize and start playback
+  Initialize();
+  Preroll();
+  StartTicking();
+  EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
+  WaitForPendingRead();
+
+  // Stop playback and flush
+  StopTicking();
+  EXPECT_TRUE(IsReadPending());
+  // Flush and expect to be notified that we have nothing.
+  EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+  FlushDuringPendingRead();
+
+  // Prepare a new demuxer stream.
+  MockDemuxerStream new_stream(DemuxerStream::AUDIO);
+  EXPECT_CALL(new_stream, SupportsConfigChanges()).WillOnce(Return(false));
+  AudioDecoderConfig audio_config(kCodec, kSampleFormat, kChannelLayout,
+                                  kInputSamplesPerSecond, EmptyExtraData(),
+                                  Unencrypted());
+  new_stream.set_audio_decoder_config(audio_config);
+
+  // The renderer is now in the flushed state and can be reinitialized.
+  WaitableMessageLoopEvent event;
+  InitializeRenderer(&new_stream, event.GetPipelineStatusCB());
+  event.RunAndWaitForStatus(PIPELINE_OK);
+}
+
 TEST_F(AudioRendererImplTest, Preroll) {
   Initialize();
   Preroll();
diff --git a/media/renderers/default_renderer_factory.cc b/media/renderers/default_renderer_factory.cc
index 5d1eb00..9bf6b6e 100644
--- a/media/renderers/default_renderer_factory.cc
+++ b/media/renderers/default_renderer_factory.cc
@@ -104,7 +104,15 @@
 
   std::unique_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl(
       media_task_runner, audio_renderer_sink,
-      CreateAudioDecoders(media_task_runner), media_log_));
+      // Unretained is safe here, because the RendererFactory is guaranteed to
+      // outlive the RendererImpl. The RendererImpl is destroyed when WMPI
+      // destructor calls pipeline_controller_.Stop() -> PipelineImpl::Stop() ->
+      // RendererWrapper::Stop -> RendererWrapper::DestroyRenderer(). And the
+      // RendererFactory is owned by WMPI and gets called after WMPI destructor
+      // finishes.
+      base::Bind(&DefaultRendererFactory::CreateAudioDecoders,
+                 base::Unretained(this), media_task_runner),
+      media_log_));
 
   GpuVideoAcceleratorFactories* gpu_factories = nullptr;
   if (!get_gpu_factories_cb_.is_null())
@@ -112,7 +120,15 @@
 
   std::unique_ptr<VideoRenderer> video_renderer(new VideoRendererImpl(
       media_task_runner, worker_task_runner, video_renderer_sink,
-      CreateVideoDecoders(media_task_runner, request_surface_cb, gpu_factories),
+      // Unretained is safe here, because the RendererFactory is guaranteed to
+      // outlive the RendererImpl. The RendererImpl is destroyed when WMPI
+      // destructor calls pipeline_controller_.Stop() -> PipelineImpl::Stop() ->
+      // RendererWrapper::Stop -> RendererWrapper::DestroyRenderer(). And the
+      // RendererFactory is owned by WMPI and gets called after WMPI destructor
+      // finishes.
+      base::Bind(&DefaultRendererFactory::CreateVideoDecoders,
+                 base::Unretained(this), media_task_runner, request_surface_cb,
+                 gpu_factories),
       true, gpu_factories, media_log_));
 
   return base::MakeUnique<RendererImpl>(
diff --git a/media/renderers/default_renderer_factory.h b/media/renderers/default_renderer_factory.h
index 7fe4a5ee..3cef53a 100644
--- a/media/renderers/default_renderer_factory.h
+++ b/media/renderers/default_renderer_factory.h
@@ -23,6 +23,11 @@
 class VideoDecoder;
 class VideoRendererSink;
 
+using CreateAudioDecodersCB =
+    base::RepeatingCallback<ScopedVector<AudioDecoder>()>;
+using CreateVideoDecodersCB =
+    base::RepeatingCallback<ScopedVector<VideoDecoder>()>;
+
 // The default factory class for creating RendererImpl.
 class MEDIA_EXPORT DefaultRendererFactory : public RendererFactory {
  public:
diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc
index cc150452..eb5710d 100644
--- a/media/renderers/renderer_impl.cc
+++ b/media/renderers/renderer_impl.cc
@@ -80,6 +80,8 @@
       task_runner_(task_runner),
       audio_renderer_(std::move(audio_renderer)),
       video_renderer_(std::move(video_renderer)),
+      current_audio_stream_(nullptr),
+      current_video_stream_(nullptr),
       time_source_(NULL),
       time_ticking_(false),
       playback_rate_(0.0),
@@ -197,10 +199,19 @@
   flush_cb_ = flush_cb;
   state_ = STATE_FLUSHING;
 
-  if (time_ticking_)
-    PausePlayback();
+  // If we are currently handling a media stream status change, then postpone
+  // Flush until after that's done (because stream status changes also flush
+  // audio_renderer_/video_renderer_ and they need to be restarted before they
+  // can be flushed again). OnStreamRestartCompleted will resume Flush
+  // processing after audio/video restart has completed and there are no other
+  // pending stream status changes.
+  if (restarting_audio_ || restarting_video_) {
+    pending_actions_.push_back(
+        base::Bind(&RendererImpl::FlushInternal, weak_this_));
+    return;
+  }
 
-  FlushAudioRenderer();
+  FlushInternal();
 }
 
 void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
@@ -221,64 +232,6 @@
     video_renderer_->StartPlayingFrom(time);
 }
 
-void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream,
-                                         bool enabled,
-                                         base::TimeDelta time) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK(stream);
-  bool video = (stream->type() == DemuxerStream::VIDEO);
-  DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream
-           << " enabled=" << enabled << " time=" << time.InSecondsF();
-  if ((state_ != STATE_PLAYING) || (audio_ended_ && video_ended_))
-    return;
-  if (restarting_audio_ || restarting_video_) {
-    DVLOG(3) << __func__ << ": postponed stream " << stream
-             << " status change handling.";
-    pending_stream_status_notifications_.push_back(
-        base::Bind(&RendererImpl::OnStreamStatusChanged, weak_this_, stream,
-                   enabled, time));
-    return;
-  }
-  if (stream->type() == DemuxerStream::VIDEO) {
-    DCHECK(video_renderer_);
-    restarting_video_ = true;
-    video_renderer_->Flush(
-        base::Bind(&RendererImpl::RestartVideoRenderer, weak_this_, time));
-  } else if (stream->type() == DemuxerStream::AUDIO) {
-    DCHECK(audio_renderer_);
-    DCHECK(time_source_);
-    restarting_audio_ = true;
-    // Stop ticking (transition into paused state) in audio renderer before
-    // calling Flush, since after Flush we are going to restart playback by
-    // calling audio renderer StartPlaying which would fail in playing state.
-    if (time_ticking_) {
-      time_ticking_ = false;
-      time_source_->StopTicking();
-    }
-    audio_renderer_->Flush(
-        base::Bind(&RendererImpl::RestartAudioRenderer, weak_this_, time));
-  }
-}
-
-void RendererImpl::RestartVideoRenderer(base::TimeDelta time) {
-  DVLOG(3) << __func__;
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK(video_renderer_);
-  DCHECK_EQ(state_, STATE_PLAYING);
-  video_ended_ = false;
-  video_renderer_->StartPlayingFrom(time);
-}
-
-void RendererImpl::RestartAudioRenderer(base::TimeDelta time) {
-  DVLOG(3) << __func__;
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK_EQ(state_, STATE_PLAYING);
-  DCHECK(time_source_);
-  DCHECK(audio_renderer_);
-  audio_ended_ = false;
-  audio_renderer_->StartPlaying();
-}
-
 void RendererImpl::SetPlaybackRate(double playback_rate) {
   DVLOG(1) << __func__ << "(" << playback_rate << ")";
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -400,6 +353,8 @@
     return;
   }
 
+  current_audio_stream_ = audio_stream;
+
   audio_renderer_client_.reset(
       new RendererClientInternal(DemuxerStream::AUDIO, this));
   // Note: After the initialization of a renderer, error events from it may
@@ -448,6 +403,8 @@
     return;
   }
 
+  current_video_stream_ = video_stream;
+
   video_renderer_client_.reset(
       new RendererClientInternal(DemuxerStream::VIDEO, this));
   video_renderer_->Initialize(
@@ -493,6 +450,18 @@
   FinishInitialization(PIPELINE_OK);
 }
 
+void RendererImpl::FlushInternal() {
+  DVLOG(1) << __func__;
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK_EQ(state_, STATE_FLUSHING);
+  DCHECK(!flush_cb_.is_null());
+
+  if (time_ticking_)
+    PausePlayback();
+
+  FlushAudioRenderer();
+}
+
 void RendererImpl::FlushAudioRenderer() {
   DVLOG(1) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -560,6 +529,168 @@
   video_ended_ = false;
   state_ = STATE_FLUSHED;
   base::ResetAndReturn(&flush_cb_).Run();
+
+  if (!pending_actions_.empty()) {
+    base::Closure closure = pending_actions_.front();
+    pending_actions_.pop_front();
+    closure.Run();
+  }
+}
+
+void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream,
+                                         bool enabled,
+                                         base::TimeDelta time) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(stream);
+  bool video = (stream->type() == DemuxerStream::VIDEO);
+  DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream
+           << " enabled=" << enabled << " time=" << time.InSecondsF();
+
+  if ((state_ != STATE_PLAYING && state_ != STATE_FLUSHING &&
+       state_ != STATE_FLUSHED) ||
+      (audio_ended_ && video_ended_))
+    return;
+
+  if (restarting_audio_ || restarting_video_ || state_ == STATE_FLUSHING) {
+    DVLOG(3) << __func__ << ": postponed stream " << stream
+             << " status change handling.";
+    pending_actions_.push_back(base::Bind(&RendererImpl::OnStreamStatusChanged,
+                                          weak_this_, stream, enabled, time));
+    return;
+  }
+
+  DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED);
+  if (stream->type() == DemuxerStream::VIDEO) {
+    DCHECK(video_renderer_);
+    restarting_video_ = true;
+    base::Closure handle_track_status_cb =
+        base::Bind(stream == current_video_stream_
+                       ? &RendererImpl::RestartVideoRenderer
+                       : &RendererImpl::ReinitializeVideoRenderer,
+                   weak_this_, stream, time);
+    if (state_ == STATE_FLUSHED)
+      handle_track_status_cb.Run();
+    else
+      video_renderer_->Flush(handle_track_status_cb);
+  } else if (stream->type() == DemuxerStream::AUDIO) {
+    DCHECK(audio_renderer_);
+    DCHECK(time_source_);
+    restarting_audio_ = true;
+    base::Closure handle_track_status_cb =
+        base::Bind(stream == current_audio_stream_
+                       ? &RendererImpl::RestartAudioRenderer
+                       : &RendererImpl::ReinitializeAudioRenderer,
+                   weak_this_, stream, time);
+    if (state_ == STATE_FLUSHED) {
+      handle_track_status_cb.Run();
+      return;
+    }
+    // Stop ticking (transition into paused state) in audio renderer before
+    // calling Flush, since after Flush we are going to restart playback by
+    // calling audio renderer StartPlaying which would fail in playing state.
+    if (time_ticking_) {
+      time_ticking_ = false;
+      time_source_->StopTicking();
+    }
+    audio_renderer_->Flush(handle_track_status_cb);
+  }
+}
+
+void RendererImpl::ReinitializeAudioRenderer(DemuxerStream* stream,
+                                             base::TimeDelta time) {
+  DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK_NE(stream, current_audio_stream_);
+
+  current_audio_stream_ = stream;
+  audio_renderer_->Initialize(
+      stream, cdm_context_, audio_renderer_client_.get(),
+      base::Bind(&RendererImpl::OnAudioRendererReinitialized, weak_this_,
+                 stream, time));
+}
+
+void RendererImpl::OnAudioRendererReinitialized(DemuxerStream* stream,
+                                                base::TimeDelta time,
+                                                PipelineStatus status) {
+  DVLOG(2) << __func__ << ": status=" << status;
+  DCHECK_EQ(stream, current_audio_stream_);
+
+  if (status != PIPELINE_OK) {
+    OnError(status);
+    return;
+  }
+  RestartAudioRenderer(stream, time);
+}
+
+void RendererImpl::ReinitializeVideoRenderer(DemuxerStream* stream,
+                                             base::TimeDelta time) {
+  DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK_NE(stream, current_video_stream_);
+
+  current_video_stream_ = stream;
+  video_renderer_->OnTimeStopped();
+  video_renderer_->Initialize(
+      stream, cdm_context_, video_renderer_client_.get(),
+      base::Bind(&RendererImpl::GetWallClockTimes, base::Unretained(this)),
+      base::Bind(&RendererImpl::OnVideoRendererReinitialized, weak_this_,
+                 stream, time));
+}
+
+void RendererImpl::OnVideoRendererReinitialized(DemuxerStream* stream,
+                                                base::TimeDelta time,
+                                                PipelineStatus status) {
+  DVLOG(2) << __func__ << ": status=" << status;
+  DCHECK_EQ(stream, current_video_stream_);
+
+  if (status != PIPELINE_OK) {
+    OnError(status);
+    return;
+  }
+  RestartVideoRenderer(stream, time);
+}
+
+void RendererImpl::RestartAudioRenderer(DemuxerStream* stream,
+                                        base::TimeDelta time) {
+  DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED ||
+         state_ == STATE_FLUSHING);
+  DCHECK(time_source_);
+  DCHECK(audio_renderer_);
+  DCHECK_EQ(stream, current_audio_stream_);
+
+  audio_ended_ = false;
+  if (state_ == STATE_FLUSHED) {
+    // If we are in the FLUSHED state, then we are done. The audio renderer will
+    // be restarted by a subsequent RendererImpl::StartPlayingFrom call.
+    OnStreamRestartCompleted();
+  } else {
+    // Stream restart will be completed when the audio renderer decodes enough
+    // data and reports HAVE_ENOUGH to HandleRestartedStreamBufferingChanges.
+    audio_renderer_->StartPlaying();
+  }
+}
+
+void RendererImpl::RestartVideoRenderer(DemuxerStream* stream,
+                                        base::TimeDelta time) {
+  DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(video_renderer_);
+  DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED ||
+         state_ == STATE_FLUSHING);
+  DCHECK_EQ(stream, current_video_stream_);
+
+  video_ended_ = false;
+  if (state_ == STATE_FLUSHED) {
+    // If we are in the FLUSHED state, then we are done. The video renderer will
+    // be restarted by a subsequent RendererImpl::StartPlayingFrom call.
+    OnStreamRestartCompleted();
+  } else {
+    // Stream restart will be completed when the video renderer decodes enough
+    // data and reports HAVE_ENOUGH to HandleRestartedStreamBufferingChanges.
+    video_renderer_->StartPlayingFrom(time);
+  }
 }
 
 void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) {
@@ -634,12 +765,16 @@
 }
 
 void RendererImpl::OnStreamRestartCompleted() {
+  DVLOG(3) << __func__ << " restarting_audio_=" << restarting_audio_
+           << " restarting_video_=" << restarting_video_;
+  DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(restarting_audio_ || restarting_video_);
   restarting_audio_ = false;
   restarting_video_ = false;
-  if (!pending_stream_status_notifications_.empty()) {
-    pending_stream_status_notifications_.front().Run();
-    pending_stream_status_notifications_.pop_front();
+  if (!pending_actions_.empty()) {
+    base::Closure closure = pending_actions_.front();
+    pending_actions_.pop_front();
+    closure.Run();
   }
 }
 
diff --git a/media/renderers/renderer_impl.h b/media/renderers/renderer_impl.h
index 0a30165..e400e8a 100644
--- a/media/renderers/renderer_impl.h
+++ b/media/renderers/renderer_impl.h
@@ -61,10 +61,6 @@
   void SetVolume(float volume) final;
   base::TimeDelta GetMediaTime() final;
 
-  void OnStreamStatusChanged(DemuxerStream* stream,
-                             bool enabled,
-                             base::TimeDelta time);
-
   // Helper functions for testing purposes. Must be called before Initialize().
   void DisableUnderflowForTesting();
   void EnableClocklessVideoPlaybackForTesting();
@@ -102,13 +98,42 @@
   void OnVideoRendererInitializeDone(PipelineStatus status);
 
   // Helper functions and callbacks for Flush().
+  void FlushInternal();
   void FlushAudioRenderer();
   void OnAudioRendererFlushDone();
   void FlushVideoRenderer();
   void OnVideoRendererFlushDone();
 
-  void RestartAudioRenderer(base::TimeDelta time);
-  void RestartVideoRenderer(base::TimeDelta time);
+  // This function notifies the renderer that the status of the demuxer |stream|
+  // has been changed, the new status is |enabled| and the change occured while
+  // playback position was |time|.
+  void OnStreamStatusChanged(DemuxerStream* stream,
+                             bool enabled,
+                             base::TimeDelta time);
+
+  // Reinitialize audio/video renderer during a demuxer stream switching. The
+  // renderer must be flushed first, and when the re-init is completed the
+  // corresponding callback will be invoked to restart playback.
+  // The |stream| parameter specifies the new demuxer stream, and the |time|
+  // parameter specifies the time on media timeline where the switch occured.
+  void ReinitializeAudioRenderer(DemuxerStream* stream, base::TimeDelta time);
+  void OnAudioRendererReinitialized(DemuxerStream* stream,
+                                    base::TimeDelta time,
+                                    PipelineStatus status);
+  void ReinitializeVideoRenderer(DemuxerStream* stream, base::TimeDelta time);
+  void OnVideoRendererReinitialized(DemuxerStream* stream,
+                                    base::TimeDelta time,
+                                    PipelineStatus status);
+
+  // Restart audio/video renderer playback after a demuxer stream switch or
+  // after a demuxer stream has been disabled and re-enabled. The |stream|
+  // parameter specifies which stream needs to be restarted. The |time|
+  // parameter specifies the position on the media timeline where the playback
+  // needs to be restarted. It is necessary for demuxers with independent
+  // streams (e.g. MSE / ChunkDemuxer) to synchronize data reading between those
+  // streams.
+  void RestartAudioRenderer(DemuxerStream* stream, base::TimeDelta time);
+  void RestartVideoRenderer(DemuxerStream* stream, base::TimeDelta time);
 
   // Callback executed by filters to update statistics.
   void OnStatisticsUpdate(const PipelineStatistics& stats);
@@ -124,6 +149,7 @@
   //     and PausePlayback() should be called
   void OnBufferingStateChange(DemuxerStream::Type type,
                               BufferingState new_buffering_state);
+
   // Handles the buffering notifications that we might get while an audio or a
   // video stream is being restarted. In those cases we don't want to report
   // underflows immediately and instead give decoders a chance to catch up with
@@ -133,6 +159,7 @@
   bool HandleRestartedStreamBufferingChanges(
       DemuxerStream::Type type,
       BufferingState new_buffering_state);
+
   bool WaitingForEnoughData() const;
   void PausePlayback();
   void StartPlayback();
@@ -167,6 +194,9 @@
   std::unique_ptr<AudioRenderer> audio_renderer_;
   std::unique_ptr<VideoRenderer> video_renderer_;
 
+  DemuxerStream* current_audio_stream_;
+  DemuxerStream* current_video_stream_;
+
   // Renderer-provided time source used to control playback.
   TimeSource* time_source_;
   std::unique_ptr<WallClockTimeSource> wall_clock_time_source_;
@@ -201,7 +231,12 @@
 
   bool restarting_audio_ = false;
   bool restarting_video_ = false;
-  std::list<base::Closure> pending_stream_status_notifications_;
+
+  // Flush operations and media track status changes must be serialized to avoid
+  // interfering with each other. This list will hold a list of postponed
+  // actions that need to be completed after the current async operation is
+  // completed.
+  std::list<base::Closure> pending_actions_;
 
   base::WeakPtr<RendererImpl> weak_this_;
   base::WeakPtrFactory<RendererImpl> weak_factory_;
diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc
index bbbaa2c..e513e69 100644
--- a/media/renderers/renderer_impl_unittest.cc
+++ b/media/renderers/renderer_impl_unittest.cc
@@ -32,6 +32,10 @@
 
 const int64_t kStartPlayingTimeInMs = 100;
 
+ACTION_P2(SetBool, var, value) {
+  *var = value;
+}
+
 ACTION_P2(SetBufferingState, renderer_client, buffering_state) {
   (*renderer_client)->OnBufferingStateChange(buffering_state);
 }
@@ -226,10 +230,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void Flush(bool underflowed) {
-    if (!underflowed)
-      EXPECT_CALL(time_source_, StopTicking());
-
+  void SetFlushExpectationsForAVRenderers() {
     if (audio_stream_) {
       EXPECT_CALL(*audio_renderer_, Flush(_))
           .WillOnce(DoAll(SetBufferingState(&audio_renderer_client_,
@@ -243,7 +244,13 @@
                                             BUFFERING_HAVE_NOTHING),
                           RunClosure<0>()));
     }
+  }
 
+  void Flush(bool underflowed) {
+    if (!underflowed)
+      EXPECT_CALL(time_source_, StopTicking());
+
+    SetFlushExpectationsForAVRenderers();
     EXPECT_CALL(callbacks_, OnFlushed());
 
     renderer_impl_->Flush(
@@ -851,4 +858,209 @@
   base::RunLoop().Run();
 }
 
+// Verify that a RendererImpl::Flush gets postponed until after stream status
+// change handling is completed.
+TEST_F(RendererImplTest, FlushDuringAudioReinit) {
+  CreateAudioAndVideoStream();
+
+  StreamStatusChangeCB stream_status_change_cb;
+  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
+      .WillOnce(SaveArg<0>(&stream_status_change_cb));
+  SetAudioRendererInitializeExpectations(PIPELINE_OK);
+  SetVideoRendererInitializeExpectations(PIPELINE_OK);
+  InitializeAndExpect(PIPELINE_OK);
+  Play();
+
+  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
+  base::Closure audio_renderer_flush_cb;
+  EXPECT_CALL(*audio_renderer_, Flush(_))
+      .WillOnce(SaveArg<0>(&audio_renderer_flush_cb));
+  EXPECT_CALL(*audio_renderer_, StartPlaying())
+      .Times(1)
+      .WillOnce(
+          SetBufferingState(&audio_renderer_client_, BUFFERING_HAVE_ENOUGH));
+
+  // This should start flushing the audio renderer (due to audio stream status
+  // change) and should populate the |audio_renderer_flush_cb|.
+  stream_status_change_cb.Run(audio_stream_.get(), false, base::TimeDelta());
+  EXPECT_TRUE(audio_renderer_flush_cb);
+  base::RunLoop().RunUntilIdle();
+
+  bool flush_done = false;
+
+  // Now that audio stream change is being handled the RendererImpl::Flush
+  // should be postponed, instead of being executed immediately.
+  EXPECT_CALL(callbacks_, OnFlushed()).WillOnce(SetBool(&flush_done, true));
+  renderer_impl_->Flush(
+      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(flush_done);
+
+  // The renderer_impl_->Flush invoked above should proceed after the first
+  // audio renderer flush (initiated by the stream status change) completes.
+  SetFlushExpectationsForAVRenderers();
+  audio_renderer_flush_cb.Run();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(flush_done);
+}
+
+TEST_F(RendererImplTest, FlushDuringVideoReinit) {
+  CreateAudioAndVideoStream();
+
+  StreamStatusChangeCB stream_status_change_cb;
+  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
+      .WillOnce(SaveArg<0>(&stream_status_change_cb));
+  SetAudioRendererInitializeExpectations(PIPELINE_OK);
+  SetVideoRendererInitializeExpectations(PIPELINE_OK);
+  InitializeAndExpect(PIPELINE_OK);
+  Play();
+
+  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
+  base::Closure video_renderer_flush_cb;
+  EXPECT_CALL(*video_renderer_, Flush(_))
+      .WillOnce(SaveArg<0>(&video_renderer_flush_cb));
+  EXPECT_CALL(*video_renderer_, StartPlayingFrom(_))
+      .Times(1)
+      .WillOnce(
+          SetBufferingState(&video_renderer_client_, BUFFERING_HAVE_ENOUGH));
+
+  // This should start flushing the video renderer (due to video stream status
+  // change) and should populate the |video_renderer_flush_cb|.
+  stream_status_change_cb.Run(video_stream_.get(), false, base::TimeDelta());
+  EXPECT_TRUE(video_renderer_flush_cb);
+  base::RunLoop().RunUntilIdle();
+
+  bool flush_done = false;
+
+  // Now that video stream change is being handled the RendererImpl::Flush
+  // should be postponed, instead of being executed immediately.
+  EXPECT_CALL(callbacks_, OnFlushed()).WillOnce(SetBool(&flush_done, true));
+  renderer_impl_->Flush(
+      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(flush_done);
+
+  // The renderer_impl_->Flush invoked above should proceed after the first
+  // video renderer flush (initiated by the stream status change) completes.
+  SetFlushExpectationsForAVRenderers();
+  video_renderer_flush_cb.Run();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(flush_done);
+}
+
+// Test audio track switching when the RendererImpl is in STATE_FLUSHING/FLUSHED
+TEST_F(RendererImplTest, AudioTrackSwitchDuringFlush) {
+  CreateAudioAndVideoStream();
+  std::unique_ptr<StrictMock<MockDemuxerStream>> primary_audio_stream =
+      std::move(audio_stream_);
+  CreateAudioStream();
+  std::unique_ptr<StrictMock<MockDemuxerStream>> secondary_audio_stream =
+      std::move(audio_stream_);
+  audio_stream_ = std::move(primary_audio_stream);
+
+  StreamStatusChangeCB stream_status_change_cb;
+  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
+      .WillOnce(SaveArg<0>(&stream_status_change_cb));
+  SetAudioRendererInitializeExpectations(PIPELINE_OK);
+  SetVideoRendererInitializeExpectations(PIPELINE_OK);
+  InitializeAndExpect(PIPELINE_OK);
+  Play();
+
+  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
+  EXPECT_CALL(*video_renderer_, Flush(_))
+      .WillRepeatedly(DoAll(
+          SetBufferingState(&video_renderer_client_, BUFFERING_HAVE_NOTHING),
+          RunClosure<0>()));
+
+  // Initiate RendererImpl::Flush, but postpone its completion by not calling
+  // audio renderer flush callback right away, i.e. pretending audio renderer
+  // flush takes a while.
+  base::Closure audio_renderer_flush_cb;
+  EXPECT_CALL(*audio_renderer_, Flush(_))
+      .WillOnce(SaveArg<0>(&audio_renderer_flush_cb));
+  EXPECT_CALL(callbacks_, OnFlushed());
+  renderer_impl_->Flush(
+      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(audio_renderer_flush_cb);
+
+  // Now, while the RendererImpl::Flush is pending, perform an audio track
+  // switch. The handling of the track switch will be postponed until after
+  // RendererImpl::Flush completes.
+  stream_status_change_cb.Run(audio_stream_.get(), false, base::TimeDelta());
+  stream_status_change_cb.Run(secondary_audio_stream.get(), true,
+                              base::TimeDelta());
+
+  // Ensure that audio track switch occurs after Flush by verifying that the
+  // audio renderer is reinitialized with the secondary audio stream.
+  EXPECT_CALL(*audio_renderer_,
+              Initialize(secondary_audio_stream.get(), _, _, _));
+
+  // Complete the audio renderer flush, thus completing the renderer_impl_ Flush
+  // initiated above. This will transition the RendererImpl into the FLUSHED
+  // state and will process pending track switch, which should result in the
+  // reinitialization of the audio renderer for the secondary audio stream.
+  audio_renderer_client_->OnBufferingStateChange(BUFFERING_HAVE_NOTHING);
+  audio_renderer_flush_cb.Run();
+  base::RunLoop().RunUntilIdle();
+}
+
+// Test video track switching when the RendererImpl is in STATE_FLUSHING/FLUSHED
+TEST_F(RendererImplTest, VideoTrackSwitchDuringFlush) {
+  CreateAudioAndVideoStream();
+  std::unique_ptr<StrictMock<MockDemuxerStream>> primary_video_stream =
+      std::move(video_stream_);
+  CreateVideoStream();
+  std::unique_ptr<StrictMock<MockDemuxerStream>> secondary_video_stream =
+      std::move(video_stream_);
+  video_stream_ = std::move(primary_video_stream);
+
+  StreamStatusChangeCB stream_status_change_cb;
+  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
+      .WillOnce(SaveArg<0>(&stream_status_change_cb));
+  SetAudioRendererInitializeExpectations(PIPELINE_OK);
+  SetVideoRendererInitializeExpectations(PIPELINE_OK);
+  InitializeAndExpect(PIPELINE_OK);
+  Play();
+
+  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
+  EXPECT_CALL(*video_renderer_, OnTimeStopped()).Times(testing::AnyNumber());
+  EXPECT_CALL(*audio_renderer_, Flush(_))
+      .WillRepeatedly(DoAll(
+          SetBufferingState(&audio_renderer_client_, BUFFERING_HAVE_NOTHING),
+          RunClosure<0>()));
+
+  // Initiate RendererImpl::Flush, but postpone its completion by not calling
+  // video renderer flush callback right away, i.e. pretending video renderer
+  // flush takes a while.
+  base::Closure video_renderer_flush_cb;
+  EXPECT_CALL(*video_renderer_, Flush(_))
+      .WillOnce(SaveArg<0>(&video_renderer_flush_cb));
+  EXPECT_CALL(callbacks_, OnFlushed());
+  renderer_impl_->Flush(
+      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(video_renderer_flush_cb);
+
+  // Now, while the RendererImpl::Flush is pending, perform a video track
+  // switch. The handling of the track switch will be postponed until after
+  // RendererImpl::Flush completes.
+  stream_status_change_cb.Run(video_stream_.get(), false, base::TimeDelta());
+  stream_status_change_cb.Run(secondary_video_stream.get(), true,
+                              base::TimeDelta());
+
+  // Ensure that video track switch occurs after Flush by verifying that the
+  // video renderer is reinitialized with the secondary video stream.
+  EXPECT_CALL(*video_renderer_,
+              Initialize(secondary_video_stream.get(), _, _, _, _));
+
+  // Complete the video renderer flush, thus completing the renderer_impl_ Flush
+  // initiated above. This will transition the RendererImpl into the FLUSHED
+  // state and will process pending track switch, which should result in the
+  // reinitialization of the video renderer for the secondary video stream.
+  video_renderer_client_->OnBufferingStateChange(BUFFERING_HAVE_NOTHING);
+  video_renderer_flush_cb.Run();
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace media
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 329437d..a755c21 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -31,7 +31,7 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
     const scoped_refptr<base::TaskRunner>& worker_task_runner,
     VideoRendererSink* sink,
-    ScopedVector<VideoDecoder> decoders,
+    const CreateVideoDecodersCB& create_video_decoders_cb,
     bool drop_frames,
     GpuVideoAcceleratorFactories* gpu_factories,
     const scoped_refptr<MediaLog>& media_log)
@@ -39,15 +39,15 @@
       sink_(sink),
       sink_started_(false),
       client_(nullptr),
-      video_frame_stream_(new VideoFrameStream(media_task_runner,
-                                               std::move(decoders),
-                                               media_log)),
       gpu_memory_buffer_pool_(nullptr),
       media_log_(media_log),
       low_delay_(false),
       received_end_of_stream_(false),
       rendered_end_of_stream_(false),
       state_(kUninitialized),
+      create_video_decoders_cb_(create_video_decoders_cb),
+      gpu_factories_(gpu_factories),
+      worker_task_runner_(worker_task_runner),
       pending_read_(false),
       drop_frames_(drop_frames),
       buffering_state_(BUFFERING_HAVE_NOTHING),
@@ -63,11 +63,7 @@
       max_buffered_frames_(limits::kMaxVideoFrames),
       weak_factory_(this),
       frame_callback_weak_factory_(this) {
-  if (gpu_factories &&
-      gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames()) {
-    gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool(
-        media_task_runner, worker_task_runner, gpu_factories));
-  }
+  DCHECK(create_video_decoders_cb_);
 }
 
 VideoRendererImpl::~VideoRendererImpl() {
@@ -150,10 +146,24 @@
   DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
   DCHECK(!init_cb.is_null());
   DCHECK(!wall_clock_time_cb.is_null());
-  DCHECK_EQ(kUninitialized, state_);
+  DCHECK(kUninitialized == state_ || kFlushed == state_);
   DCHECK(!was_background_rendering_);
   DCHECK(!time_progressing_);
-  DCHECK(!have_renderered_frames_);
+
+  ScopedVector<VideoDecoder> decoders = create_video_decoders_cb_.Run();
+  video_frame_stream_.reset(
+      new VideoFrameStream(task_runner_, std::move(decoders), media_log_));
+
+  // Always re-initialize or reset the |gpu_memory_buffer_pool_| in case we are
+  // switching between video tracks with incompatible video formats (e.g. 8-bit
+  // H.264 to 10-bit H264 or vice versa).
+  if (gpu_factories_ &&
+      gpu_factories_->ShouldUseGpuMemoryBuffersForVideoFrames()) {
+    gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool(
+        task_runner_, worker_task_runner_, gpu_factories_));
+  } else {
+    gpu_memory_buffer_pool_.reset();
+  }
 
   low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
   UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_);
diff --git a/media/renderers/video_renderer_impl.h b/media/renderers/video_renderer_impl.h
index 475167e..45af124b 100644
--- a/media/renderers/video_renderer_impl.h
+++ b/media/renderers/video_renderer_impl.h
@@ -28,6 +28,7 @@
 #include "media/base/video_renderer_sink.h"
 #include "media/filters/decoder_stream.h"
 #include "media/filters/video_renderer_algorithm.h"
+#include "media/renderers/default_renderer_factory.h"
 #include "media/renderers/gpu_video_accelerator_factories.h"
 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
 
@@ -56,7 +57,7 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
       const scoped_refptr<base::TaskRunner>& worker_task_runner,
       VideoRendererSink* sink,
-      ScopedVector<VideoDecoder> decoders,
+      const CreateVideoDecodersCB& create_video_decoders_cb,
       bool drop_frames,
       GpuVideoAcceleratorFactories* gpu_factories,
       const scoped_refptr<MediaLog>& media_log);
@@ -220,20 +221,22 @@
   // Important detail: being in kPlaying doesn't imply that video is being
   // rendered. Rather, it means that the renderer is ready to go. The actual
   // rendering of video is controlled by time advancing via |get_time_cb_|.
+  // Video renderer can be reinitialized completely by calling Initialize again
+  // when it is in a kFlushed state with video sink stopped.
   //
-  //   kUninitialized
-  //         | Initialize()
-  //         |
-  //         V
-  //    kInitializing
-  //         | Decoders initialized
-  //         |
-  //         V            Decoders reset
-  //      kFlushed <------------------ kFlushing
-  //         | StartPlayingFrom()         ^
-  //         |                            |
-  //         |                            | Flush()
-  //         `---------> kPlaying --------'
+  //    kUninitialized
+  //  +------> | Initialize()
+  //  |        |
+  //  |        V
+  //  |   kInitializing
+  //  |        | Decoders initialized
+  //  |        |
+  //  |        V            Decoders reset
+  //  ---- kFlushed <------------------ kFlushing
+  //           | StartPlayingFrom()         ^
+  //           |                            |
+  //           |                            | Flush()
+  //           `---------> kPlaying --------'
   enum State {
     kUninitialized,
     kInitializing,
@@ -243,6 +246,12 @@
   };
   State state_;
 
+  // TODO(servolk): Consider using DecoderFactory here instead of the
+  // CreateVideoDecodersCB.
+  CreateVideoDecodersCB create_video_decoders_cb_;
+  GpuVideoAcceleratorFactories* gpu_factories_;
+  scoped_refptr<base::TaskRunner> worker_task_runner_;
+
   // Keep track of the outstanding read on the VideoFrameStream. Flushing can
   // only complete once the read has completed.
   bool pending_read_;
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index 90f92188..18743170 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -53,13 +53,25 @@
 
 class VideoRendererImplTest : public testing::Test {
  public:
-  VideoRendererImplTest()
-      : tick_clock_(new base::SimpleTestTickClock()),
-        decoder_(new NiceMock<MockVideoDecoder>()),
-        demuxer_stream_(DemuxerStream::VIDEO) {
+  ScopedVector<VideoDecoder> CreateVideoDecodersForTest() {
+    decoder_ = new NiceMock<MockVideoDecoder>();
     ScopedVector<VideoDecoder> decoders;
     decoders.push_back(decoder_);
+    EXPECT_CALL(*decoder_, Initialize(_, _, _, _, _))
+        .WillOnce(DoAll(SaveArg<4>(&output_cb_),
+                        RunCallback<3>(expect_init_success_)));
+    // Monitor decodes from the decoder.
+    ON_CALL(*decoder_, Decode(_, _))
+        .WillByDefault(Invoke(this, &VideoRendererImplTest::DecodeRequested));
+    ON_CALL(*decoder_, Reset(_))
+        .WillByDefault(Invoke(this, &VideoRendererImplTest::FlushRequested));
+    return decoders;
+  }
 
+  VideoRendererImplTest()
+      : tick_clock_(new base::SimpleTestTickClock()),
+        decoder_(nullptr),
+        demuxer_stream_(DemuxerStream::VIDEO) {
     null_video_sink_.reset(new NullVideoSink(
         false, base::TimeDelta::FromSecondsD(1.0 / 60),
         base::Bind(&MockCB::FrameReceived, base::Unretained(&mock_cb_)),
@@ -67,7 +79,10 @@
 
     renderer_.reset(new VideoRendererImpl(
         message_loop_.task_runner(), message_loop_.task_runner().get(),
-        null_video_sink_.get(), std::move(decoders), true,
+        null_video_sink_.get(),
+        base::Bind(&VideoRendererImplTest::CreateVideoDecodersForTest,
+                   base::Unretained(this)),
+        true,
         nullptr,  // gpu_factories
         new MediaLog()));
     renderer_->SetTickClockForTesting(
@@ -93,34 +108,30 @@
   }
 
   void InitializeWithLowDelay(bool low_delay) {
-    // Monitor decodes from the decoder.
-    ON_CALL(*decoder_, Decode(_, _))
-        .WillByDefault(Invoke(this, &VideoRendererImplTest::DecodeRequested));
-    ON_CALL(*decoder_, Reset(_))
-        .WillByDefault(Invoke(this, &VideoRendererImplTest::FlushRequested));
-
     // Initialize, we shouldn't have any reads.
-    InitializeRenderer(low_delay, true);
+    InitializeRenderer(&demuxer_stream_, low_delay, true);
   }
 
-  void InitializeRenderer(bool low_delay, bool expect_success) {
+  void InitializeRenderer(MockDemuxerStream* demuxer_stream,
+                          bool low_delay,
+                          bool expect_success) {
     SCOPED_TRACE(base::StringPrintf("InitializeRenderer(%d)", expect_success));
+    expect_init_success_ = expect_success;
     WaitableMessageLoopEvent event;
-    CallInitialize(event.GetPipelineStatusCB(), low_delay, expect_success);
+    CallInitialize(demuxer_stream, event.GetPipelineStatusCB(), low_delay,
+                   expect_success);
     event.RunAndWaitForStatus(expect_success ? PIPELINE_OK
                                              : DECODER_ERROR_NOT_SUPPORTED);
   }
 
-  void CallInitialize(const PipelineStatusCB& status_cb,
+  void CallInitialize(MockDemuxerStream* demuxer_stream,
+                      const PipelineStatusCB& status_cb,
                       bool low_delay,
                       bool expect_success) {
     if (low_delay)
-      demuxer_stream_.set_liveness(DemuxerStream::LIVENESS_LIVE);
-    EXPECT_CALL(*decoder_, Initialize(_, _, _, _, _))
-        .WillOnce(
-            DoAll(SaveArg<4>(&output_cb_), RunCallback<3>(expect_success)));
+      demuxer_stream->set_liveness(DemuxerStream::LIVENESS_LIVE);
     EXPECT_CALL(mock_cb_, OnWaitingForDecryptionKey()).Times(0);
-    renderer_->Initialize(&demuxer_stream_, nullptr, &mock_cb_,
+    renderer_->Initialize(demuxer_stream, nullptr, &mock_cb_,
                           base::Bind(&WallClockTimeSource::GetWallClockTimes,
                                      base::Unretained(&time_source_)),
                           status_cb);
@@ -433,6 +444,8 @@
   NiceMock<MockVideoDecoder>* decoder_;    // Owned by |renderer_|.
   NiceMock<MockDemuxerStream> demuxer_stream_;
 
+  bool expect_init_success_;
+
   // Use StrictMock<T> to catch missing/extra callbacks.
   class MockCB : public MockRendererClient {
    public:
@@ -538,8 +551,18 @@
   Destroy();
 }
 
+TEST_F(VideoRendererImplTest, ReinitializeForAnotherStream) {
+  Initialize();
+  StartPlayingFrom(0);
+  Flush();
+  NiceMock<MockDemuxerStream> new_stream(DemuxerStream::VIDEO);
+  new_stream.set_video_decoder_config(TestVideoConfig::Normal());
+  InitializeRenderer(&new_stream, false, true);
+}
+
 TEST_F(VideoRendererImplTest, DestroyWhileInitializing) {
-  CallInitialize(NewExpectedStatusCB(PIPELINE_ERROR_ABORT), false, PIPELINE_OK);
+  CallInitialize(&demuxer_stream_, NewExpectedStatusCB(PIPELINE_ERROR_ABORT),
+                 false, PIPELINE_OK);
   Destroy();
 }
 
@@ -726,7 +749,7 @@
 }
 
 TEST_F(VideoRendererImplTest, VideoDecoder_InitFailure) {
-  InitializeRenderer(false, false);
+  InitializeRenderer(&demuxer_stream_, false, false);
   Destroy();
 }
 
@@ -1151,7 +1174,8 @@
 
 class VideoRendererImplAsyncAddFrameReadyTest : public VideoRendererImplTest {
  public:
-  VideoRendererImplAsyncAddFrameReadyTest() {
+  void InitializeWithMockGpuMemoryBufferVideoFramePool() {
+    VideoRendererImplTest::Initialize();
     std::unique_ptr<GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool(
         new MockGpuMemoryBufferVideoFramePool(&frame_ready_cbs_));
     renderer_->SetGpuMemoryBufferVideoForTesting(
@@ -1163,7 +1187,7 @@
 };
 
 TEST_F(VideoRendererImplAsyncAddFrameReadyTest, InitializeAndStartPlayingFrom) {
-  Initialize();
+  InitializeWithMockGpuMemoryBufferVideoFramePool();
   QueueFrames("0 10 20 30");
   EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0)));
   EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
@@ -1182,7 +1206,7 @@
 }
 
 TEST_F(VideoRendererImplAsyncAddFrameReadyTest, WeakFactoryDiscardsOneFrame) {
-  Initialize();
+  InitializeWithMockGpuMemoryBufferVideoFramePool();
   QueueFrames("0 10 20 30");
   StartPlayingFrom(0);
   Flush();
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 051a758..d51bb2e 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -685,8 +685,8 @@
 
  protected:
   std::unique_ptr<Renderer> CreateRenderer(
-      ScopedVector<VideoDecoder> prepend_video_decoders,
-      ScopedVector<AudioDecoder> prepend_audio_decoders) override {
+      CreateVideoDecodersCB prepend_video_decoders_cb,
+      CreateAudioDecodersCB prepend_audio_decoders_cb) override {
     connector()->BindInterface("media", &media_interface_factory_);
 
     mojom::RendererPtr mojo_renderer;
@@ -1134,6 +1134,30 @@
   Stop();
 }
 
+TEST_F(PipelineIntegrationTest, SwitchAudioTrackDuringPlayback) {
+  ASSERT_EQ(PIPELINE_OK, Start("multitrack-3video-2audio.webm", kHashed));
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(100)));
+  // The first audio track (TrackId=4) is enabled by default. This should
+  // disable TrackId=4 and enable TrackId=5.
+  std::vector<MediaTrack::Id> track_ids;
+  track_ids.push_back("5");
+  pipeline_->OnEnabledAudioTracksChanged(track_ids);
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
+  Stop();
+}
+
+TEST_F(PipelineIntegrationTest, SwitchVideoTrackDuringPlayback) {
+  ASSERT_EQ(PIPELINE_OK, Start("multitrack-3video-2audio.webm", kHashed));
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(100)));
+  // The first video track (TrackId=1) is enabled by default. This should
+  // disable TrackId=1 and enable TrackId=2.
+  pipeline_->OnSelectedVideoTrackChanged(MediaTrack::Id("2"));
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
+  Stop();
+}
+
 TEST_F(PipelineIntegrationTest,
        MAYBE_CLOCKLESS(BasicPlaybackOpusOggTrimmingHashed)) {
   ASSERT_EQ(PIPELINE_OK,
@@ -1658,12 +1682,15 @@
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
-TEST_F(PipelineIntegrationTest, BasicFallback) {
+ScopedVector<VideoDecoder> CreateFailingVideoDecoder() {
   ScopedVector<VideoDecoder> failing_video_decoder;
   failing_video_decoder.push_back(new FailingVideoDecoder());
+  return failing_video_decoder;
+}
 
-  ASSERT_EQ(PIPELINE_OK,
-            Start("bear.mp4", kClockless, std::move(failing_video_decoder)));
+TEST_F(PipelineIntegrationTest, BasicFallback) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear.mp4", kClockless,
+                               base::Bind(&CreateFailingVideoDecoder)));
 
   Play();
 
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 6a8b8f728..dc5f085 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -135,8 +135,8 @@
     std::unique_ptr<DataSource> data_source,
     CdmContext* cdm_context,
     uint8_t test_type,
-    ScopedVector<VideoDecoder> prepend_video_decoders,
-    ScopedVector<AudioDecoder> prepend_audio_decoders) {
+    CreateVideoDecodersCB prepend_video_decoders_cb,
+    CreateAudioDecodersCB prepend_audio_decoders_cb) {
   hashing_enabled_ = test_type & kHashed;
   clockless_playback_ = test_type & kClockless;
 
@@ -179,10 +179,11 @@
   EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
 
   pipeline_->Start(
-      demuxer_.get(), CreateRenderer(std::move(prepend_video_decoders),
-                                     std::move(prepend_audio_decoders)),
-      this, base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
-                       base::Unretained(this)));
+      demuxer_.get(),
+      CreateRenderer(prepend_video_decoders_cb, prepend_audio_decoders_cb),
+      this,
+      base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
+                 base::Unretained(this)));
   base::RunLoop().Run();
   return pipeline_status_;
 }
@@ -191,15 +192,14 @@
     const std::string& filename,
     CdmContext* cdm_context,
     uint8_t test_type,
-    ScopedVector<VideoDecoder> prepend_video_decoders,
-    ScopedVector<AudioDecoder> prepend_audio_decoders) {
+    CreateVideoDecodersCB prepend_video_decoders_cb,
+    CreateAudioDecodersCB prepend_audio_decoders_cb) {
   std::unique_ptr<FileDataSource> file_data_source(new FileDataSource());
   base::FilePath file_path(GetTestDataFilePath(filename));
   CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value()
                                                  << " missing?";
   return StartInternal(std::move(file_data_source), cdm_context, test_type,
-                       std::move(prepend_video_decoders),
-                       std::move(prepend_audio_decoders));
+                       prepend_video_decoders_cb, prepend_audio_decoders_cb);
 }
 
 PipelineStatus PipelineIntegrationTestBase::Start(const std::string& filename) {
@@ -214,11 +214,10 @@
 PipelineStatus PipelineIntegrationTestBase::Start(
     const std::string& filename,
     uint8_t test_type,
-    ScopedVector<VideoDecoder> prepend_video_decoders,
-    ScopedVector<AudioDecoder> prepend_audio_decoders) {
-  return StartWithFile(filename, nullptr, test_type,
-                       std::move(prepend_video_decoders),
-                       std::move(prepend_audio_decoders));
+    CreateVideoDecodersCB prepend_video_decoders_cb,
+    CreateAudioDecodersCB prepend_audio_decoders_cb) {
+  return StartWithFile(filename, nullptr, test_type, prepend_video_decoders_cb,
+                       prepend_audio_decoders_cb);
 }
 
 PipelineStatus PipelineIntegrationTestBase::Start(const uint8_t* data,
@@ -324,10 +323,15 @@
 #endif
 }
 
-std::unique_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
-    ScopedVector<VideoDecoder> prepend_video_decoders,
-    ScopedVector<AudioDecoder> prepend_audio_decoders) {
-  ScopedVector<VideoDecoder> video_decoders = std::move(prepend_video_decoders);
+ScopedVector<VideoDecoder> CreateVideoDecodersForTest(
+    CreateVideoDecodersCB prepend_video_decoders_cb) {
+  ScopedVector<VideoDecoder> video_decoders;
+
+  if (!prepend_video_decoders_cb.is_null()) {
+    video_decoders = prepend_video_decoders_cb.Run();
+    DCHECK(!video_decoders.empty());
+  }
+
 #if !defined(MEDIA_DISABLE_LIBVPX)
   video_decoders.push_back(new VpxVideoDecoder());
 #endif  // !defined(MEDIA_DISABLE_LIBVPX)
@@ -337,7 +341,29 @@
   video_decoders.push_back(
       new FFmpegVideoDecoder(make_scoped_refptr(new MediaLog())));
 #endif
+  return video_decoders;
+}
 
+ScopedVector<AudioDecoder> CreateAudioDecodersForTest(
+    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+    CreateAudioDecodersCB prepend_audio_decoders_cb) {
+  ScopedVector<AudioDecoder> audio_decoders;
+
+  if (!prepend_audio_decoders_cb.is_null()) {
+    audio_decoders = prepend_audio_decoders_cb.Run();
+    DCHECK(!audio_decoders.empty());
+  }
+
+#if !defined(MEDIA_DISABLE_FFMPEG)
+  audio_decoders.push_back(
+      new FFmpegAudioDecoder(media_task_runner, new MediaLog()));
+#endif
+  return audio_decoders;
+}
+
+std::unique_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
+    CreateVideoDecodersCB prepend_video_decoders_cb,
+    CreateAudioDecodersCB prepend_audio_decoders_cb) {
   // Simulate a 60Hz rendering sink.
   video_sink_.reset(new NullVideoSink(
       clockless_playback_, base::TimeDelta::FromSecondsD(1.0 / 60),
@@ -348,15 +374,9 @@
   // Disable frame dropping if hashing is enabled.
   std::unique_ptr<VideoRenderer> video_renderer(new VideoRendererImpl(
       message_loop_.task_runner(), message_loop_.task_runner().get(),
-      video_sink_.get(), std::move(video_decoders), false, nullptr,
-      new MediaLog()));
-
-  ScopedVector<AudioDecoder> audio_decoders = std::move(prepend_audio_decoders);
-
-#if !defined(MEDIA_DISABLE_FFMPEG)
-  audio_decoders.push_back(
-      new FFmpegAudioDecoder(message_loop_.task_runner(), new MediaLog()));
-#endif
+      video_sink_.get(),
+      base::Bind(&CreateVideoDecodersForTest, prepend_video_decoders_cb), false,
+      nullptr, new MediaLog()));
 
   if (!clockless_playback_) {
     audio_sink_ = new NullAudioSink(message_loop_.task_runner());
@@ -376,7 +396,9 @@
       (clockless_playback_)
           ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
           : audio_sink_.get(),
-      std::move(audio_decoders), new MediaLog()));
+      base::Bind(&CreateAudioDecodersForTest, message_loop_.task_runner(),
+                 prepend_audio_decoders_cb),
+      new MediaLog()));
   if (hashing_enabled_) {
     if (clockless_playback_)
       clockless_audio_sink_->StartAudioHashForTesting();
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 580e963c..32f72ec6 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -9,7 +9,6 @@
 #include <memory>
 
 #include "base/md5.h"
-#include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
 #include "base/test/scoped_task_scheduler.h"
 #include "media/audio/clockless_audio_sink.h"
@@ -26,9 +25,7 @@
 
 namespace media {
 
-class AudioDecoder;
 class CdmContext;
-class VideoDecoder;
 
 // Empty MD5 hash string.  Used to verify empty video tracks.
 extern const char kNullVideoHash[];
@@ -78,12 +75,12 @@
   // started. |filename| points at a test file located under media/test/data/.
   PipelineStatus Start(const std::string& filename);
   PipelineStatus Start(const std::string& filename, CdmContext* cdm_context);
-  PipelineStatus Start(const std::string& filename,
-                       uint8_t test_type,
-                       ScopedVector<VideoDecoder> prepend_video_decoders =
-                           ScopedVector<VideoDecoder>(),
-                       ScopedVector<AudioDecoder> prepend_audio_decoders =
-                           ScopedVector<AudioDecoder>());
+  PipelineStatus Start(
+      const std::string& filename,
+      uint8_t test_type,
+      CreateVideoDecodersCB prepend_video_decoders_cb = CreateVideoDecodersCB(),
+      CreateAudioDecodersCB prepend_audio_decoders_cb =
+          CreateAudioDecodersCB());
 
   // Starts the pipeline with |data| (with |size| bytes). The |data| will be
   // valid throughtout the lifetime of this test.
@@ -157,19 +154,17 @@
       std::unique_ptr<DataSource> data_source,
       CdmContext* cdm_context,
       uint8_t test_type,
-      ScopedVector<VideoDecoder> prepend_video_decoders =
-          ScopedVector<VideoDecoder>(),
-      ScopedVector<AudioDecoder> prepend_audio_decoders =
-          ScopedVector<AudioDecoder>());
+      CreateVideoDecodersCB prepend_video_decoders_cb = CreateVideoDecodersCB(),
+      CreateAudioDecodersCB prepend_audio_decoders_cb =
+          CreateAudioDecodersCB());
 
   PipelineStatus StartWithFile(
       const std::string& filename,
       CdmContext* cdm_context,
       uint8_t test_type,
-      ScopedVector<VideoDecoder> prepend_video_decoders =
-          ScopedVector<VideoDecoder>(),
-      ScopedVector<AudioDecoder> prepend_audio_decoders =
-          ScopedVector<AudioDecoder>());
+      CreateVideoDecodersCB prepend_video_decoders_cb = CreateVideoDecodersCB(),
+      CreateAudioDecodersCB prepend_audio_decoders_cb =
+          CreateAudioDecodersCB());
 
   void OnSeeked(base::TimeDelta seek_time, PipelineStatus status);
   void OnStatusCallback(PipelineStatus status);
@@ -185,10 +180,9 @@
 
   // Creates and returns a Renderer.
   virtual std::unique_ptr<Renderer> CreateRenderer(
-      ScopedVector<VideoDecoder> prepend_video_decoders =
-          ScopedVector<VideoDecoder>(),
-      ScopedVector<AudioDecoder> prepend_audio_decoders =
-          ScopedVector<AudioDecoder>());
+      CreateVideoDecodersCB prepend_video_decoders_cb = CreateVideoDecodersCB(),
+      CreateAudioDecodersCB prepend_audio_decoders_cb =
+          CreateAudioDecodersCB());
 
   void OnVideoFramePaint(const scoped_refptr<VideoFrame>& frame);
 
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.h b/media/video/gpu_memory_buffer_video_frame_pool.h
index ed4b361..b7c16b5a 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.h
+++ b/media/video/gpu_memory_buffer_video_frame_pool.h
@@ -24,6 +24,11 @@
 // The pool recycles resources to a void unnecessarily allocating and
 // destroying textures, images and GpuMemoryBuffer that could result
 // in a round trip to the browser/GPU process.
+// NOTE: Destroying the pool will not immediately invalidate outstanding video
+// frames. GPU memory buffers will be kept alive by video frames indirectly
+// referencing them. Video frames themselves are ref-counted and will be
+// released when they are no longer needed, potentially after the pool is
+// destroyed.
 class MEDIA_EXPORT GpuMemoryBufferVideoFramePool {
  public:
   GpuMemoryBufferVideoFramePool();
diff --git a/mojo/common/values_struct_traits.h b/mojo/common/values_struct_traits.h
index 4139ce6c..7d8c2ed 100644
--- a/mojo/common/values_struct_traits.h
+++ b/mojo/common/values_struct_traits.h
@@ -17,7 +17,7 @@
 
 template <>
 struct ArrayTraits<base::ListValue> {
-  using Element = std::unique_ptr<base::Value>;
+  using Element = base::Value;
   using ConstIterator = base::ListValue::const_iterator;
 
   static size_t GetSize(const base::ListValue& input) {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 6320cca..2a7a44f 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -3381,6 +3381,7 @@
     "data/url_request_unittest/with-headers.html.mock-http-headers",
     "data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued.pem",
     "data/verify_certificate_chain_unittest/constrained-non-self-signed-root.pem",
+    "data/verify_certificate_chain_unittest/constrained-root-bad-eku.pem",
     "data/verify_certificate_chain_unittest/constrained-root-basic-constraints-ca-false.pem",
     "data/verify_certificate_chain_unittest/constrained-root-lacks-basic-constraints.pem",
     "data/verify_certificate_chain_unittest/expired-constrained-root.pem",
@@ -3393,6 +3394,9 @@
     "data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical.pem",
     "data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints.pem",
     "data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage.pem",
+    "data/verify_certificate_chain_unittest/intermediate-restricts-eku-fail.pem",
+    "data/verify_certificate_chain_unittest/intermediate-restricts-eku-ok.pem",
+    "data/verify_certificate_chain_unittest/intermediate-sets-eku-any.pem",
     "data/verify_certificate_chain_unittest/intermediate-signed-with-md5.pem",
     "data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension.pem",
     "data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension.pem",
@@ -3406,13 +3410,17 @@
     "data/verify_certificate_chain_unittest/target-and-intermediate.pem",
     "data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca.pem",
     "data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca.pem",
+    "data/verify_certificate_chain_unittest/target-lacks-eku.pem",
     "data/verify_certificate_chain_unittest/target-not-end-entity.pem",
+    "data/verify_certificate_chain_unittest/target-restricts-eku-fail.pem",
+    "data/verify_certificate_chain_unittest/target-sets-eku-any.pem",
     "data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa.pem",
     "data/verify_certificate_chain_unittest/target-signed-using-ecdsa.pem",
     "data/verify_certificate_chain_unittest/target-signed-with-md5.pem",
     "data/verify_certificate_chain_unittest/target-unknown-critical-extension.pem",
     "data/verify_certificate_chain_unittest/target-wrong-signature.pem",
     "data/verify_certificate_chain_unittest/unconstrained-non-self-signed-root.pem",
+    "data/verify_certificate_chain_unittest/unconstrained-root-bad-eku.pem",
     "data/verify_certificate_chain_unittest/unconstrained-root-basic-constraints-ca-false.pem",
     "data/verify_certificate_chain_unittest/unconstrained-root-lacks-basic-constraints.pem",
     "data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0.pem",
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index 12b82a4..08a2feed 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -359,7 +359,8 @@
   // Initialize the path builder.
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target, trust_store->GetTrustStore(),
-                               &signature_policy, verification_time, &result);
+                               &signature_policy, verification_time,
+                               KeyPurpose::SERVER_AUTH, &result);
 
   // Allow the path builder to discover intermediates from the trust store.
   if (trust_store->GetCertIssuerSource())
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index c59c246..fdb254d 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -49,6 +49,9 @@
 #include "base/win/windows_version.h"
 #endif
 
+// TODO(crbug.com/649017): Add tests that only certificates with
+// serverAuth are accepted.
+
 using net::test::IsError;
 using net::test::IsOk;
 
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index 71192b9..291bcf5 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -491,10 +491,12 @@
                                  const TrustStore* trust_store,
                                  const SignaturePolicy* signature_policy,
                                  const der::GeneralizedTime& time,
+                                 KeyPurpose key_purpose,
                                  Result* result)
     : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)),
       signature_policy_(signature_policy),
       time_(time),
+      key_purpose_(key_purpose),
       next_state_(STATE_NONE),
       out_result_(result) {}
 
@@ -541,9 +543,9 @@
 
   // Verify the entire certificate chain.
   auto result_path = base::MakeUnique<ResultPath>();
-  bool verify_result =
-      VerifyCertificateChain(next_path_.certs, next_path_.trust_anchor.get(),
-                             signature_policy_, time_, &result_path->errors);
+  bool verify_result = VerifyCertificateChain(
+      next_path_.certs, next_path_.trust_anchor.get(), signature_policy_, time_,
+      key_purpose_, &result_path->errors);
   DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
            << verify_result;
   result_path->path = next_path_;
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h
index d9d32fc..361e745 100644
--- a/net/cert/internal/path_builder.h
+++ b/net/cert/internal/path_builder.h
@@ -13,6 +13,7 @@
 #include "net/cert/internal/cert_errors.h"
 #include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
 #include "net/der/input.h"
 #include "net/der/parse_values.h"
 
@@ -118,6 +119,7 @@
                   const TrustStore* trust_store,
                   const SignaturePolicy* signature_policy,
                   const der::GeneralizedTime& time,
+                  KeyPurpose key_purpose,
                   Result* result);
   ~CertPathBuilder();
 
@@ -152,6 +154,7 @@
   std::unique_ptr<CertPathIter> cert_path_iter_;
   const SignaturePolicy* signature_policy_;
   const der::GeneralizedTime time_;
+  const KeyPurpose key_purpose_;
 
   // Stores the next complete path to attempt verification on. This is filled in
   // by |cert_path_iter_| during the STATE_GET_NEXT_PATH step, and thus should
diff --git a/net/cert/internal/path_builder_pkits_unittest.cc b/net/cert/internal/path_builder_pkits_unittest.cc
index 2d16ee9b..a7b02e3 100644
--- a/net/cert/internal/path_builder_pkits_unittest.cc
+++ b/net/cert/internal/path_builder_pkits_unittest.cc
@@ -92,7 +92,8 @@
 
     CertPathBuilder::Result result;
     CertPathBuilder path_builder(std::move(target_cert), &trust_store,
-                                 &signature_policy, time, &result);
+                                 &signature_policy, time, KeyPurpose::ANY_EKU,
+                                 &result);
     path_builder.AddCertIssuerSource(&cert_issuer_source);
 
     path_builder.Run();
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc
index d207d44..44471fa8 100644
--- a/net/cert/internal/path_builder_unittest.cc
+++ b/net/cert/internal/path_builder_unittest.cc
@@ -160,7 +160,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -180,7 +180,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -210,7 +210,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(b_by_c_, &trust_store, &signature_policy_,
-                               expired_time, &result);
+                               expired_time, KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -243,7 +243,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(e_by_e_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -262,7 +262,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -289,7 +289,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&async_certs);
   path_builder.AddCertIssuerSource(&sync_certs);
 
@@ -317,7 +317,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&async_certs1);
   path_builder.AddCertIssuerSource(&async_certs2);
   path_builder.AddCertIssuerSource(&sync_certs);
@@ -344,7 +344,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -377,7 +377,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
   path_builder.AddCertIssuerSource(&async_certs);
 
@@ -416,7 +416,7 @@
 
     CertPathBuilder::Result result;
     CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_,
-                                 time_, &result);
+                                 time_, KeyPurpose::ANY_EKU, &result);
     path_builder.AddCertIssuerSource(&sync_certs);
 
     path_builder.Run();
@@ -505,7 +505,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -553,7 +553,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -589,7 +589,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -621,7 +621,8 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store_collection,
-                               &signature_policy_, time_, &result);
+                               &signature_policy_, time_, KeyPurpose::ANY_EKU,
+                               &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -671,7 +672,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
   path_builder.AddCertIssuerSource(&async_certs);
 
@@ -728,7 +729,8 @@
   CertPathBuilder::Result result;
   // Newintermediate is also the target cert.
   CertPathBuilder path_builder(newintermediate_, &trust_store,
-                               &signature_policy_, time_, &result);
+                               &signature_policy_, time_, KeyPurpose::ANY_EKU,
+                               &result);
 
   path_builder.Run();
 
@@ -752,7 +754,7 @@
   CertPathBuilder::Result result;
   // Newroot is the target cert.
   CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_,
-                               time_, &result);
+                               time_, KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -773,7 +775,7 @@
   CertPathBuilder::Result result;
   // Newroot is the target cert.
   CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_,
-                               time_, &result);
+                               time_, KeyPurpose::ANY_EKU, &result);
 
   path_builder.Run();
 
@@ -821,7 +823,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs1);
   path_builder.AddCertIssuerSource(&sync_certs2);
   path_builder.AddCertIssuerSource(&async_certs);
@@ -875,7 +877,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&sync_certs);
 
   path_builder.Run();
@@ -950,7 +952,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&cert_issuer_source);
 
   // Create the mock CertIssuerSource::Request...
@@ -1029,7 +1031,7 @@
 
   CertPathBuilder::Result result;
   CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
-                               &result);
+                               KeyPurpose::ANY_EKU, &result);
   path_builder.AddCertIssuerSource(&cert_issuer_source);
 
   // Create the mock CertIssuerSource::Request...
diff --git a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
index 3c404bed..43ba879 100644
--- a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
@@ -30,7 +30,8 @@
     CertPathBuilder::Result result;
     // First cert in the |chain| is the target.
     CertPathBuilder path_builder(test.chain.front(), &trust_store,
-                                 &signature_policy, test.time, &result);
+                                 &signature_policy, test.time, test.key_purpose,
+                                 &result);
     path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
 
     path_builder.Run();
diff --git a/net/cert/internal/test_helpers.cc b/net/cert/internal/test_helpers.cc
index f6a65d3e4..80948c2 100644
--- a/net/cert/internal/test_helpers.cc
+++ b/net/cert/internal/test_helpers.cc
@@ -174,11 +174,11 @@
       has_key_purpose = true;
 
       if (block_data == "anyExtendedKeyUsage") {
-        // TODO(eroman): test->key_purpose = ....
+        test->key_purpose = KeyPurpose::ANY_EKU;
       } else if (block_data == "serverAuth") {
-        // TODO(eroman): test->key_purpose = ....
+        test->key_purpose = KeyPurpose::SERVER_AUTH;
       } else if (block_data == "clientAuth") {
-        // TODO(eroman): test->key_purpose = ....
+        test->key_purpose = KeyPurpose::CLIENT_AUTH;
       } else {
         ADD_FAILURE() << "Unrecognized " << block_type << ": " << block_data;
       }
diff --git a/net/cert/internal/test_helpers.h b/net/cert/internal/test_helpers.h
index 0cd2f70d..25afc288 100644
--- a/net/cert/internal/test_helpers.h
+++ b/net/cert/internal/test_helpers.h
@@ -13,6 +13,7 @@
 
 #include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
 #include "net/der/input.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -91,6 +92,9 @@
   // The time to use when verifying the chain.
   der::GeneralizedTime time;
 
+  // The Key Purpose to use when verifying the chain.
+  KeyPurpose key_purpose = KeyPurpose::ANY_EKU;
+
   // The expected result from verification.
   bool expected_result = false;
 
diff --git a/net/cert/internal/trust_store.h b/net/cert/internal/trust_store.h
index 6985301..383e83a54 100644
--- a/net/cert/internal/trust_store.h
+++ b/net/cert/internal/trust_store.h
@@ -75,7 +75,7 @@
   //  * Signature:             No
   //  * Validity (expiration): No
   //  * Key usage:             No
-  //  * Extended key usage:    No
+  //  * Extended key usage:    Yes (not part of RFC 5937)
   //  * Basic constraints:     Yes, but only the pathlen (CA=false is accepted)
   //  * Name constraints:      Yes
   //  * Certificate policies:  Not currently, TODO(crbug.com/634453)
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index cc9da69..5f2b7d81 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "net/cert/internal/cert_error_params.h"
 #include "net/cert/internal/cert_errors.h"
+#include "net/cert/internal/extended_key_usage.h"
 #include "net/cert/internal/name_constraints.h"
 #include "net/cert/internal/parse_certificate.h"
 #include "net/cert/internal/signature_algorithm.h"
@@ -55,12 +56,18 @@
 DEFINE_CERT_ERROR_ID(kSignatureAlgorithmsDifferentEncoding,
                      "Certificate.signatureAlgorithm is encoded differently "
                      "than TBSCertificate.signature");
+DEFINE_CERT_ERROR_ID(kEkuLacksServerAuth,
+                     "The extended key usage does not include server auth");
+DEFINE_CERT_ERROR_ID(kEkuLacksClientAuth,
+                     "The extended key usage does not include client auth");
 
 bool IsHandledCriticalExtensionOid(const der::Input& oid) {
   if (oid == BasicConstraintsOid())
     return true;
   if (oid == KeyUsageOid())
     return true;
+  if (oid == ExtKeyUsageOid())
+    return true;
   if (oid == NameConstraintsOid())
     return true;
   // TODO(eroman): SubjectAltName isn't actually used here, but rather is being
@@ -165,6 +172,46 @@
                                 "TBSCertificate.signature", alg2_tlv));
 }
 
+// Verify that |cert| can be used for |required_key_purpose|.
+void VerifyExtendedKeyUsage(const ParsedCertificate& cert,
+                            KeyPurpose required_key_purpose,
+                            CertErrors* errors) {
+  switch (required_key_purpose) {
+    case KeyPurpose::ANY_EKU:
+      return;
+    case KeyPurpose::SERVER_AUTH: {
+      // TODO(eroman): Is it OK for the target certificate to omit the EKU?
+      if (!cert.has_extended_key_usage())
+        return;
+
+      for (const auto& key_purpose_oid : cert.extended_key_usage()) {
+        if (key_purpose_oid == AnyEKU())
+          return;
+        if (key_purpose_oid == ServerAuth())
+          return;
+      }
+
+      errors->AddError(kEkuLacksServerAuth);
+      break;
+    }
+    case KeyPurpose::CLIENT_AUTH: {
+      // TODO(eroman): Is it OK for the target certificate to omit the EKU?
+      if (!cert.has_extended_key_usage())
+        return;
+
+      for (const auto& key_purpose_oid : cert.extended_key_usage()) {
+        if (key_purpose_oid == AnyEKU())
+          return;
+        if (key_purpose_oid == ClientAuth())
+          return;
+      }
+
+      errors->AddError(kEkuLacksClientAuth);
+      break;
+    }
+  }
+}
+
 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
 // Processing" procedure.
 void BasicCertificateProcessing(
@@ -395,6 +442,7 @@
 // follows the description in RFC 5937
 void ProcessTrustAnchorConstraints(
     const TrustAnchor& trust_anchor,
+    KeyPurpose required_key_purpose,
     size_t* max_path_length_ptr,
     std::vector<const NameConstraints*>* name_constraints_list,
     CertErrors* errors) {
@@ -408,6 +456,10 @@
   // Anchor constraints are encoded via the attached certificate.
   const ParsedCertificate& cert = *trust_anchor.cert();
 
+  // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling
+  // done for intermediates (described in Web PKI's Baseline Requirements).
+  VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
+
   // The following enforcements follow from RFC 5937 (primarily section 3.2):
 
   // Initialize name constraints initial-permitted/excluded-subtrees.
@@ -452,6 +504,7 @@
     const TrustAnchor* trust_anchor,
     const SignaturePolicy* signature_policy,
     const der::GeneralizedTime& time,
+    KeyPurpose required_key_purpose,
     CertPathErrors* errors) {
   DCHECK(trust_anchor);
   DCHECK(signature_policy);
@@ -507,8 +560,8 @@
   // TODO(eroman): Errors on the trust anchor are put into a certificate bucket
   //               GetErrorsForCert(certs.size()). This is a bit magical, and
   //               has some integration issues.
-  ProcessTrustAnchorConstraints(*trust_anchor, &max_path_length,
-                                &name_constraints_list,
+  ProcessTrustAnchorConstraints(*trust_anchor, required_key_purpose,
+                                &max_path_length, &name_constraints_list,
                                 errors->GetErrorsForCert(certs.size()));
 
   // Iterate over all the certificates in the reverse direction: starting from
@@ -541,12 +594,21 @@
     BasicCertificateProcessing(cert, is_target_cert, signature_policy, time,
                                working_spki, working_normalized_issuer_name,
                                name_constraints_list, cert_errors);
+
+    // The key purpose is checked not just for the end-entity certificate, but
+    // also interpreted as a constraint when it appears in intermediates. This
+    // goes beyond what RFC 5280 describes, but is the de-facto standard. See
+    // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
+    VerifyExtendedKeyUsage(cert, required_key_purpose, cert_errors);
+
     if (!is_target_cert) {
       PrepareForNextCertificate(cert, &max_path_length, &working_spki,
                                 &working_normalized_issuer_name,
                                 &name_constraints_list, cert_errors);
     } else {
       WrapUp(cert, cert_errors);
+      // TODO(eroman): Verify the Key Usage on target is consistent with
+      //               key_purpose.
     }
   }
 
@@ -562,12 +624,13 @@
                             const TrustAnchor* trust_anchor,
                             const SignaturePolicy* signature_policy,
                             const der::GeneralizedTime& time,
+                            KeyPurpose required_key_purpose,
                             CertPathErrors* errors) {
   // TODO(eroman): This function requires that |errors| is empty upon entry,
   // which is not part of the API contract.
   DCHECK(!errors->ContainsHighSeverityErrors());
   VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy,
-                                      time, errors);
+                                      time, required_key_purpose, errors);
   return !errors->ContainsHighSeverityErrors();
 }
 
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index 5ebecb5..7abeede 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -23,6 +23,13 @@
 class SignaturePolicy;
 class TrustAnchor;
 
+// The key purpose (extended key usage) to check for during verification.
+enum class KeyPurpose {
+  ANY_EKU,
+  SERVER_AUTH,
+  CLIENT_AUTH,
+};
+
 // VerifyCertificateChain() verifies a certificate path (chain) based on the
 // rules in RFC 5280. The caller is responsible for building the path and
 // finding the trust anchor.
@@ -56,6 +63,9 @@
 //   time:
 //     The UTC time to use for expiration checks.
 //
+//   key_purpose:
+//     The key purpose that the target certificate needs to be valid for.
+//
 // ---------
 // Outputs
 // ---------
@@ -72,6 +82,7 @@
                                        const TrustAnchor* trust_anchor,
                                        const SignaturePolicy* signature_policy,
                                        const der::GeneralizedTime& time,
+                                       KeyPurpose required_key_purpose,
                                        CertPathErrors* errors);
 
 // TODO(crbug.com/634443): Move exported errors to a central location?
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index e8279104..97a81cc 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -81,7 +81,8 @@
 
     CertPathErrors path_errors;
     bool result = VerifyCertificateChain(input_chain, trust_anchor.get(),
-                                         &signature_policy, time, &path_errors);
+                                         &signature_policy, time,
+                                         KeyPurpose::ANY_EKU, &path_errors);
 
     //  TODO(crbug.com/634443): Test errors on failure?
     EXPECT_EQ(result, !path_errors.ContainsHighSeverityErrors());
diff --git a/net/cert/internal/verify_certificate_chain_typed_unittest.h b/net/cert/internal/verify_certificate_chain_typed_unittest.h
index 1861241..48e40186 100644
--- a/net/cert/internal/verify_certificate_chain_typed_unittest.h
+++ b/net/cert/internal/verify_certificate_chain_typed_unittest.h
@@ -8,6 +8,7 @@
 #include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/internal/test_helpers.h"
 #include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
 #include "net/cert/pem_tokenizer.h"
 #include "net/der/input.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,6 +24,8 @@
     std::string path =
         std::string("net/data/verify_certificate_chain_unittest/") + file_name;
 
+    SCOPED_TRACE("Test file: " + path);
+
     ReadVerifyCertChainTestFromFile(path, &test);
 
     TestDelegate::Verify(test, path);
@@ -37,60 +40,42 @@
 
 TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest);
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetAndIntermediate) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Simple) {
   this->RunTest("target-and-intermediate.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateLacksBasicConstraints) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, BasicConstraintsCa) {
   this->RunTest("intermediate-lacks-basic-constraints.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateBasicConstraintsCaFalse) {
   this->RunTest("intermediate-basic-constraints-ca-false.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateBasicConstraintsNotCritical) {
   this->RunTest("intermediate-basic-constraints-not-critical.pem");
+  this->RunTest("unconstrained-root-lacks-basic-constraints.pem");
+  this->RunTest("constrained-root-lacks-basic-constraints.pem");
+  this->RunTest("unconstrained-root-basic-constraints-ca-false.pem");
+  this->RunTest("constrained-root-basic-constraints-ca-false.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateLacksSigningKeyUsage) {
-  this->RunTest("intermediate-lacks-signing-key-usage.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateUnknownCriticalExtension) {
-  this->RunTest("intermediate-unknown-critical-extension.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IntermediateUnknownNonCriticalExtension) {
-  this->RunTest("intermediate-unknown-non-critical-extension.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ViolatesBasicConstraintsPathlen0) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, BasicConstraintsPathlen) {
   this->RunTest("violates-basic-constraints-pathlen-0.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             BasicConstraintsPathlen0SelfIssued) {
   this->RunTest("basic-constraints-pathlen-0-self-issued.pem");
+  this->RunTest("target-has-pathlen-but-not-ca.pem");
+  this->RunTest("violates-pathlen-1-constrained-root.pem");
+  this->RunTest("violates-pathlen-1-unconstrained-root.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedWithMd5) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, UnknownExtension) {
+  this->RunTest("intermediate-unknown-critical-extension.pem");
+  this->RunTest("intermediate-unknown-non-critical-extension.pem");
+  this->RunTest("target-unknown-critical-extension.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Md5) {
   this->RunTest("target-signed-with-md5.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, IntermediateSignedWithMd5) {
   this->RunTest("intermediate-signed-with-md5.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetWrongSignature) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WrongSignature) {
   this->RunTest("target-wrong-signature.pem");
+  this->RunTest("incorrect-trust-anchor.pem");
 }
 
 TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedBy512bitRsa) {
@@ -101,23 +86,11 @@
   this->RunTest("target-signed-using-ecdsa.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredIntermediate) {
-  this->RunTest("expired-intermediate.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTarget) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Expired) {
   this->RunTest("expired-target.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTargetNotBefore) {
+  this->RunTest("expired-intermediate.pem");
   this->RunTest("expired-target-notBefore.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredUnconstrainedRoot) {
   this->RunTest("expired-unconstrained-root.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredConstrainedRoot) {
   this->RunTest("expired-constrained-root.pem");
 }
 
@@ -125,138 +98,60 @@
   this->RunTest("target-not-end-entity.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             TargetHasKeyCertSignButNotCa) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyUsage) {
+  this->RunTest("intermediate-lacks-signing-key-usage.pem");
   this->RunTest("target-has-keycertsign-but-not-ca.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetHasPathlenButNotCa) {
-  this->RunTest("target-has-pathlen-but-not-ca.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             TargetUnknownCriticalExtension) {
-  this->RunTest("target-unknown-critical-extension.pem");
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExtendedKeyUsage) {
+  this->RunTest("target-lacks-eku.pem");
+  this->RunTest("target-restricts-eku-fail.pem");
+  this->RunTest("intermediate-restricts-eku-fail.pem");
+  this->RunTest("intermediate-restricts-eku-ok.pem");
+  this->RunTest("intermediate-sets-eku-any.pem");
+  this->RunTest("target-sets-eku-any.pem");
+  this->RunTest("constrained-root-bad-eku.pem");
+  this->RunTest("unconstrained-root-bad-eku.pem");
 }
 
 TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
              IssuerAndSubjectNotByteForByteEqual) {
   this->RunTest("issuer-and-subject-not-byte-for-byte-equal.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             IssuerAndSubjectNotByteForByteEqualAnchor) {
   this->RunTest("issuer-and-subject-not-byte-for-byte-equal-anchor.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ViolatesPathlen1UnconstrainedRoot) {
-  this->RunTest("violates-pathlen-1-unconstrained-root.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ViolatesPathlen1ConstrainedRoot) {
-  this->RunTest("violates-pathlen-1-constrained-root.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, NonSelfSignedRoot) {
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TrustAnchorNotSelfSigned) {
   this->RunTest("non-self-signed-root.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverOldChain) {
-  this->RunTest("key-rollover-oldchain.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverRolloverChain) {
-  this->RunTest("key-rollover-rolloverchain.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             KeyRolloverLongRolloverChain) {
-  this->RunTest("key-rollover-longrolloverchain.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverNewChain) {
-  this->RunTest("key-rollover-newchain.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest, IncorrectTrustAnchor) {
-  this->RunTest("incorrect-trust-anchor.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             UnconstrainedRootLacksBasicConstraints) {
-  this->RunTest("unconstrained-root-lacks-basic-constraints.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ConstrainedRootLacksBasicConstraints) {
-  this->RunTest("constrained-root-lacks-basic-constraints.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             UnconstrainedRootBasicConstraintsCaFalse) {
-  this->RunTest("unconstrained-root-basic-constraints-ca-false.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ConstrainedRootBasicConstraintsCaFalse) {
-  this->RunTest("constrained-root-basic-constraints-ca-false.pem");
-}
-
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             UnconstrainedNonSelfSignedRoot) {
   this->RunTest("unconstrained-non-self-signed-root.pem");
+  this->RunTest("constrained-non-self-signed-root.pem");
 }
 
-TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
-             ConstrainedNonSelfSignedRoot) {
-  this->RunTest("constrained-non-self-signed-root.pem");
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRollover) {
+  this->RunTest("key-rollover-oldchain.pem");
+  this->RunTest("key-rollover-rolloverchain.pem");
+  this->RunTest("key-rollover-longrolloverchain.pem");
+  this->RunTest("key-rollover-newchain.pem");
 }
 
 // TODO(eroman): Add test that invalid validity dates where the day or month
 // ordinal not in range, like "March 39, 2016" are rejected.
 
 REGISTER_TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest,
-                           TargetAndIntermediate,
-                           IntermediateLacksBasicConstraints,
-                           IntermediateBasicConstraintsCaFalse,
-                           IntermediateBasicConstraintsNotCritical,
-                           IntermediateLacksSigningKeyUsage,
-                           IntermediateUnknownCriticalExtension,
-                           IntermediateUnknownNonCriticalExtension,
-                           ViolatesBasicConstraintsPathlen0,
-                           BasicConstraintsPathlen0SelfIssued,
-                           TargetSignedWithMd5,
-                           IntermediateSignedWithMd5,
-                           TargetWrongSignature,
+                           Simple,
+                           BasicConstraintsCa,
+                           BasicConstraintsPathlen,
+                           UnknownExtension,
+                           Md5,
+                           WrongSignature,
                            TargetSignedBy512bitRsa,
                            TargetSignedUsingEcdsa,
-                           ExpiredIntermediate,
-                           ExpiredTarget,
-                           ExpiredTargetNotBefore,
-                           ExpiredUnconstrainedRoot,
-                           ExpiredConstrainedRoot,
+                           Expired,
                            TargetNotEndEntity,
-                           TargetHasKeyCertSignButNotCa,
-                           TargetHasPathlenButNotCa,
-                           TargetUnknownCriticalExtension,
+                           KeyUsage,
+                           ExtendedKeyUsage,
                            IssuerAndSubjectNotByteForByteEqual,
-                           IssuerAndSubjectNotByteForByteEqualAnchor,
-                           ViolatesPathlen1UnconstrainedRoot,
-                           ViolatesPathlen1ConstrainedRoot,
-                           NonSelfSignedRoot,
-                           KeyRolloverOldChain,
-                           KeyRolloverRolloverChain,
-                           KeyRolloverLongRolloverChain,
-                           KeyRolloverNewChain,
-                           IncorrectTrustAnchor,
-                           UnconstrainedRootLacksBasicConstraints,
-                           ConstrainedRootLacksBasicConstraints,
-                           UnconstrainedRootBasicConstraintsCaFalse,
-                           ConstrainedRootBasicConstraintsCaFalse,
-                           UnconstrainedNonSelfSignedRoot,
-                           ConstrainedNonSelfSignedRoot);
+                           TrustAnchorNotSelfSigned,
+                           KeyRollover);
 
 }  // namespace net
 
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc
index 7a162b8..62d3b3b 100644
--- a/net/cert/internal/verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -22,7 +22,8 @@
 
     CertPathErrors errors;
     bool result = VerifyCertificateChain(test.chain, test.trust_anchor.get(),
-                                         &signature_policy, test.time, &errors);
+                                         &signature_policy, test.time,
+                                         test.key_purpose, &errors);
     EXPECT_EQ(test.expected_result, result);
     EXPECT_EQ(test.expected_errors, errors.ToDebugString(test.chain))
         << "Test file: " << test_file_path;
diff --git a/net/data/verify_certificate_chain_unittest/constrained-root-bad-eku.pem b/net/data/verify_certificate_chain_unittest/constrained-root-bad-eku.pem
new file mode 100644
index 0000000..5fe160a
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/constrained-root-bad-eku.pem
@@ -0,0 +1,299 @@
+[Created by: generate-constrained-root-bad-eku.py]
+
+Certificate chain with 1 intermediate and a trust anchor. The trust anchor
+has an EKU that restricts it to clientAuth. Verification is expected to fail as
+the end-entity is verified for serverAuth, and the trust anchor enforces
+constraints.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:f0:08:00:ea:5d:93:fb:9f:3d:fd:b5:ae:6f:89:
+                    02:b9:7f:4b:75:b9:51:cb:ef:6f:dd:7b:50:b6:2a:
+                    a7:fa:9c:41:88:6e:a1:be:2b:8b:54:2a:02:c8:c0:
+                    2c:ed:c8:ad:75:9d:84:22:c5:12:d8:63:ac:60:85:
+                    42:3d:e2:c5:59:00:01:c7:4d:63:08:bf:a2:63:cf:
+                    dd:fc:48:e6:55:e6:2c:5c:d6:bf:e1:d1:19:09:56:
+                    8b:43:f2:be:ba:04:81:33:7d:5c:ee:26:3b:f7:c2:
+                    15:d5:57:11:4c:08:fc:48:e4:f5:8b:d1:62:cb:72:
+                    10:7e:fe:ae:84:ff:f8:d6:35:20:80:f3:b9:59:a3:
+                    7f:1d:bf:6f:f5:6d:6b:29:e4:b1:5e:2e:20:cc:80:
+                    04:f8:6d:67:04:18:71:ac:c3:cf:53:4b:ca:1a:a1:
+                    06:c1:7d:d7:fe:24:a8:6b:d2:52:18:4a:7a:ad:c4:
+                    2f:70:e1:a8:66:9a:94:dc:13:b2:26:4d:e0:60:f1:
+                    67:57:31:f1:00:d5:b2:3c:31:6a:34:52:75:2b:d2:
+                    f3:d3:b0:d6:f7:54:be:9c:ba:99:39:82:50:02:ee:
+                    b6:d8:c4:b7:ce:08:30:a7:8e:2d:b0:6b:78:f1:19:
+                    27:cd:c5:c3:a4:f2:c7:91:b3:5e:61:94:e6:a7:94:
+                    3b:c7
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                F2:29:C1:07:7D:FB:62:7D:1B:06:45:F8:E8:F4:FE:77:B2:8C:BC:AE
+            X509v3 Authority Key Identifier: 
+                keyid:8B:3C:5F:76:85:CD:27:14:00:7B:0B:92:AF:4A:D5:52:9B:BA:53:BE
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         3a:2e:7b:e3:cd:20:6b:b0:dc:d6:a3:4f:00:98:53:76:bd:36:
+         82:12:c8:ed:11:8a:31:17:90:55:45:07:0d:72:4e:94:dd:a6:
+         22:ad:c0:b9:ee:4e:5f:d5:0d:1c:62:d8:04:5d:75:75:10:ed:
+         e1:4b:79:16:cf:bd:b1:2e:bc:0d:1b:10:0c:4b:77:8f:61:51:
+         7f:41:fb:35:2d:5c:2d:b4:51:15:01:68:51:72:ae:ec:eb:bb:
+         f8:e1:45:7a:80:5c:e5:5b:c5:c0:27:1d:12:7a:d5:80:be:06:
+         64:38:f7:59:57:f5:c8:54:aa:42:0f:71:f0:d5:b9:a3:dc:7e:
+         e0:e8:08:d2:d3:6d:12:aa:51:24:fa:5d:58:64:73:a7:8a:b6:
+         4a:83:9c:a2:12:04:4c:cb:2f:40:e6:6e:e1:b2:fe:1a:d0:7e:
+         2c:fd:e8:21:5c:08:fe:e8:d4:81:cd:07:2f:c1:ca:96:c8:79:
+         2e:53:30:36:41:8e:bd:49:95:76:1e:18:b6:53:a5:45:d0:08:
+         b4:e7:21:13:bc:f1:21:00:18:34:dc:d1:86:71:ea:05:70:54:
+         ac:42:89:20:e1:9f:4a:0e:11:09:00:bf:fd:46:91:3e:13:14:
+         7c:c6:68:e1:df:c1:16:5d:3f:e9:2f:91:dd:17:0a:e4:95:3d:
+         a6:96:e4:cf
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDwCADq
+XZP7nz39ta5viQK5f0t1uVHL72/de1C2Kqf6nEGIbqG+K4tUKgLIwCztyK11nYQi
+xRLYY6xghUI94sVZAAHHTWMIv6Jjz938SOZV5ixc1r/h0RkJVotD8r66BIEzfVzu
+Jjv3whXVVxFMCPxI5PWL0WLLchB+/q6E//jWNSCA87lZo38dv2/1bWsp5LFeLiDM
+gAT4bWcEGHGsw89TS8oaoQbBfdf+JKhr0lIYSnqtxC9w4ahmmpTcE7ImTeBg8WdX
+MfEA1bI8MWo0UnUr0vPTsNb3VL6cupk5glAC7rbYxLfOCDCnji2wa3jxGSfNxcOk
+8seRs15hlOanlDvHAgMBAAGjgekwgeYwHQYDVR0OBBYEFPIpwQd9+2J9GwZF+Oj0
+/neyjLyuMB8GA1UdIwQYMBaAFIs8X3aFzScUAHsLkq9K1VKbulO+MD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAOi57480ga7Dc1qNPAJhT
+dr02ghLI7RGKMReQVUUHDXJOlN2mIq3Aue5OX9UNHGLYBF11dRDt4Ut5Fs+9sS68
+DRsQDEt3j2FRf0H7NS1cLbRRFQFoUXKu7Ou7+OFFeoBc5VvFwCcdEnrVgL4GZDj3
+WVf1yFSqQg9x8NW5o9x+4OgI0tNtEqpRJPpdWGRzp4q2SoOcohIETMsvQOZu4bL+
+GtB+LP3oIVwI/ujUgc0HL8HKlsh5LlMwNkGOvUmVdh4YtlOlRdAItOchE7zxIQAY
+NNzRhnHqBXBUrEKJIOGfSg4RCQC//UaRPhMUfMZo4d/BFl0/6S+R3RcK5JU9ppbk
+zw==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b6:0a:f3:0b:43:2a:d8:e5:15:f6:48:94:21:e9:
+                    23:61:0f:0e:cc:5a:a6:ba:45:b0:8b:99:54:44:0b:
+                    56:4e:b1:13:e2:ce:b9:58:0a:dc:77:41:36:86:ac:
+                    eb:b4:54:04:77:1f:cf:f6:1d:12:c5:58:38:65:ff:
+                    20:41:f0:43:9d:a4:bf:fa:61:3b:75:52:f0:39:9d:
+                    5b:fb:e2:88:fb:69:32:b2:b9:4d:a8:34:88:e3:ce:
+                    e1:2f:f4:03:d5:1b:3f:1a:ab:95:98:a1:6f:87:2b:
+                    10:76:02:4f:ba:67:7f:9a:23:5c:90:8a:dc:b3:27:
+                    a5:28:14:98:1e:b2:06:92:0a:60:37:fe:56:b6:16:
+                    84:59:01:a5:e9:1d:04:d5:46:66:e4:30:fa:0e:0e:
+                    c2:d7:66:21:4a:fc:99:4f:85:33:96:36:7d:dc:5c:
+                    04:16:bc:5c:ee:f3:6d:4d:b6:a2:0f:39:fc:e2:63:
+                    96:bf:3d:5a:61:02:8f:db:d7:07:c4:24:02:f0:02:
+                    52:e7:2c:08:78:b9:8d:d9:5f:2d:cc:6c:1e:9e:f9:
+                    91:95:e8:be:13:77:02:b3:86:ab:cb:24:ed:4a:bf:
+                    36:29:2d:66:36:1e:fc:3d:3a:c5:0f:23:5d:e9:2e:
+                    41:d8:79:97:ee:8b:cc:75:2d:c7:3a:be:4d:e5:fd:
+                    a2:33
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                8B:3C:5F:76:85:CD:27:14:00:7B:0B:92:AF:4A:D5:52:9B:BA:53:BE
+            X509v3 Authority Key Identifier: 
+                keyid:1A:DD:A6:8B:40:A4:5B:6A:1B:06:BF:9B:76:54:8A:9B:88:F8:8B:07
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         83:84:de:b3:56:ad:2a:16:56:6e:7b:64:3e:35:bd:39:38:2e:
+         6e:2a:fa:09:a9:c3:ea:86:30:8c:4e:e0:2b:13:80:a1:40:f3:
+         10:15:9d:4f:77:90:0f:12:c9:a5:60:2e:87:43:0b:c1:90:5a:
+         b3:95:fb:37:0c:c7:86:d0:2f:bb:4c:b8:97:40:d6:61:a6:47:
+         e6:30:42:9f:e6:28:ac:b7:99:83:52:a0:c0:4b:dd:e2:ad:1b:
+         e7:5d:c5:9a:fb:6c:d9:bc:7c:bc:64:1a:47:9b:01:9f:4e:10:
+         aa:f7:6a:25:a0:0b:64:6b:8f:54:42:74:23:d3:83:a8:b7:fc:
+         78:95:65:11:27:f2:b5:1e:90:78:31:9d:f3:5f:7f:8d:63:3f:
+         ce:cf:1e:11:bd:8b:01:a7:fa:33:d2:9b:ac:9c:c0:ee:b1:f2:
+         ee:02:ea:73:07:28:1d:8c:23:98:93:cc:23:92:26:35:a4:d7:
+         57:f7:d1:28:b0:4e:6a:9c:78:01:c9:f2:52:e0:1d:13:86:76:
+         7e:13:3c:07:69:a5:3f:d6:3e:2e:36:70:0a:be:4d:1a:14:ac:
+         73:bd:5a:ad:78:68:a6:35:a2:50:5b:ab:c1:e3:a1:f7:47:f2:
+         76:2b:e8:5d:4a:e4:f3:4a:dc:93:53:64:9c:83:f3:af:a5:0f:
+         e1:83:1d:89
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtgrzC0Mq
+2OUV9kiUIekjYQ8OzFqmukWwi5lURAtWTrET4s65WArcd0E2hqzrtFQEdx/P9h0S
+xVg4Zf8gQfBDnaS/+mE7dVLwOZ1b++KI+2kysrlNqDSI487hL/QD1Rs/GquVmKFv
+hysQdgJPumd/miNckIrcsyelKBSYHrIGkgpgN/5WthaEWQGl6R0E1UZm5DD6Dg7C
+12YhSvyZT4UzljZ93FwEFrxc7vNtTbaiDzn84mOWvz1aYQKP29cHxCQC8AJS5ywI
+eLmN2V8tzGwenvmRlei+E3cCs4aryyTtSr82KS1mNh78PTrFDyNd6S5B2HmX7ovM
+dS3HOr5N5f2iMwIDAQABo4HLMIHIMB0GA1UdDgQWBBSLPF92hc0nFAB7C5KvStVS
+m7pTvjAfBgNVHSMEGDAWgBQa3aaLQKRbahsGv5t2VIqbiPiLBzA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AIOE3rNWrSoWVm57ZD41vTk4Lm4q+gmpw+qGMIxO4CsTgKFA8xAVnU93kA8SyaVg
+LodDC8GQWrOV+zcMx4bQL7tMuJdA1mGmR+YwQp/mKKy3mYNSoMBL3eKtG+ddxZr7
+bNm8fLxkGkebAZ9OEKr3aiWgC2Rrj1RCdCPTg6i3/HiVZREn8rUekHgxnfNff41j
+P87PHhG9iwGn+jPSm6ycwO6x8u4C6nMHKB2MI5iTzCOSJjWk11f30SiwTmqceAHJ
+8lLgHROGdn4TPAdppT/WPi42cAq+TRoUrHO9Wq14aKY1olBbq8HjofdH8nYr6F1K
+5PNK3JNTZJyD86+lD+GDHYk=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:d9:e2:08:ac:46:4f:3e:c9:2c:0e:1b:2e:0d:cb:
+                    05:2e:b2:60:dd:39:3b:31:90:3c:89:ee:6f:32:3f:
+                    4e:9c:4a:93:d7:97:e5:e9:9d:0a:72:a5:77:c8:e6:
+                    67:db:e0:e2:d7:35:ab:d5:7b:26:2a:97:39:4c:04:
+                    e3:32:93:df:69:9e:5e:c7:fb:3a:53:70:18:91:39:
+                    76:23:aa:65:5b:e0:87:32:cb:2c:6c:6f:e7:38:9f:
+                    79:db:23:ea:3c:86:9b:f2:03:d3:df:15:5c:ce:58:
+                    b5:46:77:5d:21:09:f9:e3:ae:16:ce:e6:d5:95:41:
+                    d5:ee:c7:74:89:bf:dc:c8:80:47:e0:49:6e:ff:26:
+                    6b:0a:d4:c2:04:21:a0:b5:b0:07:4d:1b:1c:e1:a8:
+                    53:23:13:3f:01:31:d9:3f:dc:2d:70:8b:61:49:b1:
+                    6d:6f:c6:4e:f7:35:45:17:40:39:9d:28:1d:77:68:
+                    82:c2:75:9a:c2:9f:90:ab:4c:c9:8a:3e:68:2a:2c:
+                    ba:62:ab:e1:58:c8:3b:fe:c0:95:8e:55:33:53:ca:
+                    3f:fb:9a:ae:95:13:65:3f:a6:9a:d6:98:f1:ad:72:
+                    74:ce:d8:65:12:9f:63:fd:63:c5:3f:90:3d:d8:b2:
+                    2b:fe:48:fa:da:ab:f2:49:c6:1d:2a:ba:f8:73:e1:
+                    50:a1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                1A:DD:A6:8B:40:A4:5B:6A:1B:06:BF:9B:76:54:8A:9B:88:F8:8B:07
+            X509v3 Authority Key Identifier: 
+                keyid:1A:DD:A6:8B:40:A4:5B:6A:1B:06:BF:9B:76:54:8A:9B:88:F8:8B:07
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         7f:58:39:4d:ec:e0:7e:11:fa:c1:29:d1:c8:56:42:19:33:f4:
+         8c:e0:a1:22:90:fc:9d:cc:d2:36:4f:f7:91:51:cc:0a:40:49:
+         da:cc:70:81:3e:59:ae:65:a3:c5:86:42:5f:df:fe:1d:51:93:
+         fb:77:99:01:b0:02:c5:95:1f:32:6f:a2:4a:21:28:50:f8:bc:
+         5d:67:01:28:a0:4f:6c:a0:43:ea:7b:7a:66:3a:33:a0:c2:0c:
+         a5:44:10:9b:e4:f9:4a:09:43:02:e0:01:ca:fd:c2:b1:07:31:
+         c8:6b:0d:ec:c8:c1:4f:53:2e:10:1b:d9:8a:42:00:74:d5:cc:
+         ec:47:51:c5:12:63:a7:f2:93:4f:0e:cd:82:3c:70:3b:9f:c8:
+         0c:9f:5b:fa:15:47:e5:e6:6d:5d:37:7c:fa:e2:a2:4b:aa:d8:
+         be:c4:2e:e5:3e:71:ae:c9:7b:79:86:1c:29:3c:00:e3:d5:9b:
+         30:23:12:c0:33:12:7d:36:8c:99:cb:6a:39:74:fa:8f:6e:8f:
+         5c:53:6e:53:94:59:c9:59:7d:1e:3c:e2:ac:32:43:5e:4c:14:
+         87:cf:39:c9:55:38:e0:29:a6:19:e9:62:21:8d:f0:1b:9d:31:
+         c9:c3:93:12:fd:b3:0e:83:fc:21:dc:bb:df:09:a6:57:6e:18:
+         58:ff:ad:73
+-----BEGIN TRUST_ANCHOR_CONSTRAINED-----
+MIIDejCCAmKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANniCKxGTz7JLA4bLg3L
+BS6yYN05OzGQPInubzI/TpxKk9eX5emdCnKld8jmZ9vg4tc1q9V7JiqXOUwE4zKT
+32meXsf7OlNwGJE5diOqZVvghzLLLGxv5zifedsj6jyGm/ID098VXM5YtUZ3XSEJ
++eOuFs7m1ZVB1e7HdIm/3MiAR+BJbv8mawrUwgQhoLWwB00bHOGoUyMTPwEx2T/c
+LXCLYUmxbW/GTvc1RRdAOZ0oHXdogsJ1msKfkKtMyYo+aCosumKr4VjIO/7AlY5V
+M1PKP/uarpUTZT+mmtaY8a1ydM7YZRKfY/1jxT+QPdiyK/5I+tqr8knGHSq6+HPh
+UKECAwEAAaOB4DCB3TAdBgNVHQ4EFgQUGt2mi0CkW2obBr+bdlSKm4j4iwcwHwYD
+VR0jBBgwFoAUGt2mi0CkW2obBr+bdlSKm4j4iwcwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqG
+SIb3DQEBCwUAA4IBAQB/WDlN7OB+EfrBKdHIVkIZM/SM4KEikPydzNI2T/eRUcwK
+QEnazHCBPlmuZaPFhkJf3/4dUZP7d5kBsALFlR8yb6JKIShQ+LxdZwEooE9soEPq
+e3pmOjOgwgylRBCb5PlKCUMC4AHK/cKxBzHIaw3syMFPUy4QG9mKQgB01czsR1HF
+EmOn8pNPDs2CPHA7n8gMn1v6FUfl5m1dN3z64qJLqti+xC7lPnGuyXt5hhwpPADj
+1ZswIxLAMxJ9NoyZy2o5dPqPbo9cU25TlFnJWX0ePOKsMkNeTBSHzznJVTjgKaYZ
+6WIhjfAbnTHJw5MS/bMOg/wh3LvfCaZXbhhY/61z
+-----END TRUST_ANCHOR_CONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+FAIL
+-----BEGIN VERIFY_RESULT-----
+RkFJTA==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
+
+----- Certificate i=2 -----
+ERROR: The extended key usage does not include server auth
+
+
+-----BEGIN ERRORS-----
+LS0tLS0gQ2VydGlmaWNhdGUgaT0yIC0tLS0tCkVSUk9SOiBUaGUgZXh0ZW5kZWQga2V5IHVzYWdlIGRvZXMgbm90IGluY2x1ZGUgc2VydmVyIGF1dGgKCg==
+-----END ERRORS-----
diff --git a/net/data/verify_certificate_chain_unittest/generate-constrained-root-bad-eku.py b/net/data/verify_certificate_chain_unittest/generate-constrained-root-bad-eku.py
new file mode 100755
index 0000000..ae9db3b
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-constrained-root-bad-eku.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trust anchor. The trust anchor
+has an EKU that restricts it to clientAuth. Verification is expected to fail as
+the end-entity is verified for serverAuth, and the trust anchor enforces
+constraints."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor) with non-CA basic
+# constraints.
+root = common.create_self_signed_root_certificate('Root')
+root.get_extensions().set_property('extendedKeyUsage', 'clientAuth')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=True)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = False
+errors = """----- Certificate i=2 -----
+ERROR: The extended key usage does not include server auth
+
+"""
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-fail.py b/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-fail.py
new file mode 100755
index 0000000..1465979
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-fail.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to clientAuth, and the target has serverAuth +
+clientAuth. Verification is expected to fail when requesting serverAuth."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+intermediate.get_extensions().set_property('extendedKeyUsage',
+                                           'clientAuth')
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().set_property('extendedKeyUsage',
+                                     'serverAuth,clientAuth')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = False
+errors = """----- Certificate i=1 (CN=Intermediate) -----
+ERROR: The extended key usage does not include server auth
+
+"""
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-ok.py b/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-ok.py
new file mode 100755
index 0000000..ef5b82ac
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-intermediate-restricts-eku-ok.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to serverAuth, and the target has serverAuth +
+clientAuth. Verification is expected to succeed as this is consistent with
+the requested key purpose."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+intermediate.get_extensions().set_property('extendedKeyUsage',
+                                           'serverAuth')
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().set_property('extendedKeyUsage',
+                                     'serverAuth,clientAuth')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = True
+errors = None
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-intermediate-sets-eku-any.py b/net/data/verify_certificate_chain_unittest/generate-intermediate-sets-eku-any.py
new file mode 100755
index 0000000..01ae7a93
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-intermediate-sets-eku-any.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to clientAuth + any, and the target has serverAuth +
+clientAuth. Verification is expected to succeed because intermediate will match
+the "any"."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+intermediate.get_extensions().set_property('extendedKeyUsage',
+                                           'clientAuth,anyExtendedKeyUsage')
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().set_property('extendedKeyUsage',
+                                     'serverAuth,clientAuth')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = True
+errors = None
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-target-lacks-eku.py b/net/data/verify_certificate_chain_unittest/generate-target-lacks-eku.py
new file mode 100755
index 0000000..846b481
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-target-lacks-eku.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The target has no
+Extended Key Usage extension (meaning it is unrestricted). Verification is
+expected to succeed."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().remove_property('extendedKeyUsage')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = True
+errors = None
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-target-restricts-eku-fail.py b/net/data/verify_certificate_chain_unittest/generate-target-restricts-eku-fail.py
new file mode 100755
index 0000000..43a6846ca
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-target-restricts-eku-fail.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The target
+certificate has only clientAuth EKU, so is expected to fail when verifying for
+serverAuth."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().set_property('extendedKeyUsage', 'clientAuth')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = False
+errors = """----- Certificate i=0 (CN=Target) -----
+ERROR: The extended key usage does not include server auth
+
+"""
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-target-sets-eku-any.py b/net/data/verify_certificate_chain_unittest/generate-target-sets-eku-any.py
new file mode 100755
index 0000000..cf9c8575
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-target-sets-eku-any.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trusted root. The target
+restricts EKU to clientAuth+any and requests serverAuth during verification.
+This should succeed."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor).
+root = common.create_self_signed_root_certificate('Root')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+target.get_extensions().set_property('extendedKeyUsage',
+                                     'clientAuth,anyExtendedKeyUsage')
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = True
+errors = None
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/generate-unconstrained-root-bad-eku.py b/net/data/verify_certificate_chain_unittest/generate-unconstrained-root-bad-eku.py
new file mode 100755
index 0000000..4da8905b
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-unconstrained-root-bad-eku.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""Certificate chain with 1 intermediate and a trust anchor. The trust anchor
+has an EKU that restricts it to clientAuth. Verification is expected to fail as
+the end-entity is verified for serverAuth, and the trust anchor enforces
+constraints."""
+
+import common
+
+# Self-signed root certificate (used as trust anchor) with non-CA basic
+# constraints.
+root = common.create_self_signed_root_certificate('Root')
+root.get_extensions().set_property('extendedKeyUsage', 'clientAuth')
+
+# Intermediate certificate.
+intermediate = common.create_intermediate_certificate('Intermediate', root)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', intermediate)
+
+chain = [target, intermediate]
+trusted = common.TrustAnchor(root, constrained=False)
+time = common.DEFAULT_TIME
+key_purpose = common.KEY_PURPOSE_SERVER_AUTH
+verify_result = True
+errors = None
+
+common.write_test_file(__doc__, chain, trusted, time, key_purpose,
+                       verify_result, errors)
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-fail.pem b/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-fail.pem
new file mode 100644
index 0000000..80580ad
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-fail.pem
@@ -0,0 +1,298 @@
+[Created by: generate-intermediate-restricts-eku-fail.py]
+
+Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to clientAuth, and the target has serverAuth +
+clientAuth. Verification is expected to fail when requesting serverAuth.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:bb:d3:3c:f5:4c:df:73:61:c9:d0:be:56:b8:7f:
+                    e6:52:56:9c:3b:84:83:23:d8:ea:30:cb:cc:01:ba:
+                    1d:36:70:d3:4c:58:62:74:2f:96:57:7c:e5:b0:27:
+                    6f:fa:72:c0:5b:0b:0c:f6:ec:1e:3b:c7:04:45:b8:
+                    89:97:be:fa:49:27:b6:c2:0a:29:b8:98:cd:a4:a4:
+                    54:29:ce:55:c5:91:ff:89:d3:51:87:88:d0:c3:ef:
+                    0c:de:43:b0:e0:b9:d9:23:92:f0:04:42:b6:50:06:
+                    2b:1a:7b:97:3e:67:a4:ed:77:23:e5:83:76:76:63:
+                    09:6d:be:05:6e:fc:aa:a0:c8:91:97:97:2d:85:02:
+                    95:c2:fc:dd:dc:f4:4b:08:c3:be:3b:43:76:96:cc:
+                    ec:55:7a:0f:00:fe:29:4b:87:ca:df:50:ba:5c:60:
+                    e5:6f:8c:f0:56:7b:5b:20:3d:87:fd:81:7f:61:51:
+                    6c:44:61:55:3a:52:28:cf:49:4d:72:3f:34:b0:a3:
+                    04:18:e6:47:50:c7:f0:e1:a5:4f:8c:59:e3:73:ca:
+                    b6:a6:0d:34:a3:40:fb:41:97:8c:66:93:64:29:20:
+                    13:1b:f5:ab:69:74:11:88:13:8d:dc:15:c8:22:a2:
+                    2b:16:74:f2:f1:8b:27:c1:5a:9c:c5:0e:95:78:ba:
+                    fe:9f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                6D:1B:79:D9:7C:01:F2:1D:99:D4:DD:54:90:BF:32:03:0F:28:4D:38
+            X509v3 Authority Key Identifier: 
+                keyid:3A:B9:4C:96:D7:3D:14:A8:24:C8:DE:55:0A:54:05:5D:5C:A2:C9:99
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         03:b3:7d:3a:ad:31:3c:23:cd:8f:44:99:81:8c:1a:72:a7:c9:
+         da:24:74:8c:cf:00:59:8b:d6:38:d1:b0:64:e6:9b:bb:40:9a:
+         d0:d0:ba:22:58:4d:a1:4d:16:7e:a9:dd:27:11:b5:69:31:5d:
+         7d:cb:8d:26:16:76:3f:fc:f4:15:72:c0:50:32:88:2c:83:02:
+         b6:6f:c3:e7:a0:93:53:f6:e5:e9:6b:bc:91:9c:18:3a:ae:4f:
+         86:5b:b3:88:bd:1b:1e:29:cf:13:76:f8:5c:93:50:32:c5:a1:
+         96:0a:fb:b9:66:53:a4:5a:e6:e8:fe:75:90:02:68:18:82:cf:
+         0e:1f:37:6c:47:43:5d:4b:10:68:16:89:4a:59:f4:2a:62:1e:
+         7c:7a:35:a2:5b:0e:72:f1:7b:62:d8:84:bd:ad:8c:1e:4d:71:
+         d3:45:aa:2f:0c:46:bd:06:0f:88:38:14:11:d5:c6:e8:d8:82:
+         f1:c0:b6:0a:f6:c7:d0:71:89:1c:4f:11:d2:ae:cc:ee:b6:39:
+         01:69:46:69:8b:73:4f:d3:ad:2a:a8:be:49:7c:01:22:58:b9:
+         d1:63:10:5f:19:d9:51:22:45:13:24:48:91:8a:00:9a:70:54:
+         91:3a:ab:65:ef:63:b8:ce:48:1c:b7:9d:1a:a3:9c:9a:96:ce:
+         51:3f:88:8e
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC70zz1
+TN9zYcnQvla4f+ZSVpw7hIMj2Oowy8wBuh02cNNMWGJ0L5ZXfOWwJ2/6csBbCwz2
+7B47xwRFuImXvvpJJ7bCCim4mM2kpFQpzlXFkf+J01GHiNDD7wzeQ7DgudkjkvAE
+QrZQBisae5c+Z6TtdyPlg3Z2YwltvgVu/KqgyJGXly2FApXC/N3c9EsIw747Q3aW
+zOxVeg8A/ilLh8rfULpcYOVvjPBWe1sgPYf9gX9hUWxEYVU6UijPSU1yPzSwowQY
+5kdQx/DhpU+MWeNzyramDTSjQPtBl4xmk2QpIBMb9atpdBGIE43cFcgioisWdPLx
+iyfBWpzFDpV4uv6fAgMBAAGjgekwgeYwHQYDVR0OBBYEFG0bedl8AfIdmdTdVJC/
+MgMPKE04MB8GA1UdIwQYMBaAFDq5TJbXPRSoJMjeVQpUBV1cosmZMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAA7N9Oq0xPCPNj0SZgYwa
+cqfJ2iR0jM8AWYvWONGwZOabu0Ca0NC6IlhNoU0WfqndJxG1aTFdfcuNJhZ2P/z0
+FXLAUDKILIMCtm/D56CTU/bl6Wu8kZwYOq5PhluziL0bHinPE3b4XJNQMsWhlgr7
+uWZTpFrm6P51kAJoGILPDh83bEdDXUsQaBaJSln0KmIefHo1olsOcvF7YtiEva2M
+Hk1x00WqLwxGvQYPiDgUEdXG6NiC8cC2CvbH0HGJHE8R0q7M7rY5AWlGaYtzT9Ot
+Kqi+SXwBIli50WMQXxnZUSJFEyRIkYoAmnBUkTqrZe9juM5IHLedGqOcmpbOUT+I
+jg==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:bd:9a:08:67:72:a5:4d:ba:39:c4:0a:d5:a9:42:
+                    46:7a:a0:f3:f2:2b:1f:83:91:58:a7:00:3b:b3:17:
+                    51:e5:1f:83:13:44:10:14:7f:84:6d:97:57:de:32:
+                    00:bd:15:18:e4:c7:89:8b:6e:5b:41:51:ad:d3:c9:
+                    f7:3e:75:51:74:5c:71:40:2e:9b:95:be:8f:3b:17:
+                    33:a5:3a:33:17:97:05:d7:30:0c:40:94:c1:8d:e7:
+                    80:5f:f3:d4:3e:e4:46:8c:e3:80:ec:95:91:87:e0:
+                    a0:a3:32:73:6c:44:c2:9c:12:a5:d3:6b:91:e0:60:
+                    3d:a1:61:9d:09:6f:5f:7b:b1:c5:98:6a:3a:cc:85:
+                    76:45:f2:44:0e:3f:cf:b9:56:5a:23:55:68:31:4b:
+                    17:30:ad:a0:e2:b1:85:3f:6e:2e:7e:a7:38:b9:dd:
+                    cd:3d:fb:74:1a:83:87:c2:ec:ec:6a:63:0b:5e:c8:
+                    75:07:b5:4f:3f:93:58:a5:fe:3e:76:18:ee:16:df:
+                    b1:52:b8:1a:f0:77:65:a3:b7:2d:16:a3:e6:c8:11:
+                    67:e1:20:ea:2f:ed:0b:93:e6:c8:2a:a0:fc:34:b7:
+                    fa:4b:21:33:60:02:86:cf:b4:bd:f0:c7:ec:f5:7a:
+                    b4:ff:84:18:f4:73:a1:28:7a:31:de:08:b6:fd:be:
+                    0a:7d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                3A:B9:4C:96:D7:3D:14:A8:24:C8:DE:55:0A:54:05:5D:5C:A2:C9:99
+            X509v3 Authority Key Identifier: 
+                keyid:AE:89:01:94:41:77:67:BD:EF:7F:98:4F:29:E7:1B:3A:18:B9:DD:51
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         48:57:46:43:a9:be:bb:bb:5c:b1:c1:42:cd:72:22:1e:34:98:
+         8f:ae:c5:8f:88:72:3e:01:f4:9f:d0:2a:ed:6b:3a:32:22:e0:
+         9e:be:2c:7d:5c:b8:01:98:d8:41:97:58:88:69:a1:1c:2d:c3:
+         97:00:71:47:b8:f9:91:5d:66:b2:cd:e9:ec:3c:1a:70:b5:7a:
+         ee:8e:88:d3:d6:c3:32:e7:11:c2:a5:8d:b4:01:2c:87:06:a5:
+         c8:85:07:29:6c:1a:20:ee:5e:ca:6e:42:f0:89:f3:e9:d1:e8:
+         cd:d6:4b:ef:b4:fc:10:be:ae:b8:4c:0d:b0:af:3d:72:4a:17:
+         03:72:d5:aa:a4:30:bf:8d:6f:66:9f:19:fa:e0:c7:e4:fe:5c:
+         30:53:95:8e:6a:87:59:bb:14:62:3a:1a:2f:24:87:c3:0c:8e:
+         09:e7:e4:f1:3c:e3:03:e7:29:28:5d:b4:fd:a1:f7:8c:00:c5:
+         8f:33:56:09:df:48:f8:d3:1e:cd:4a:b8:69:ac:81:65:3d:20:
+         f3:d2:52:dd:44:e7:fa:f8:22:b4:fb:ec:8e:b9:27:a8:d9:12:
+         69:bc:d0:6d:b6:45:41:c5:d6:fc:16:d0:47:da:b4:a3:75:f0:
+         e2:fa:49:9d:5f:9c:64:ee:17:1d:1a:83:26:5d:e0:ad:fb:a8:
+         74:70:30:08
+-----BEGIN CERTIFICATE-----
+MIIDgjCCAmqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvZoIZ3Kl
+Tbo5xArVqUJGeqDz8isfg5FYpwA7sxdR5R+DE0QQFH+EbZdX3jIAvRUY5MeJi25b
+QVGt08n3PnVRdFxxQC6blb6POxczpTozF5cF1zAMQJTBjeeAX/PUPuRGjOOA7JWR
+h+CgozJzbETCnBKl02uR4GA9oWGdCW9fe7HFmGo6zIV2RfJEDj/PuVZaI1VoMUsX
+MK2g4rGFP24ufqc4ud3NPft0GoOHwuzsamMLXsh1B7VPP5NYpf4+dhjuFt+xUrga
+8Hdlo7ctFqPmyBFn4SDqL+0Lk+bIKqD8NLf6SyEzYAKGz7S98Mfs9Xq0/4QY9HOh
+KHox3gi2/b4KfQIDAQABo4HgMIHdMB0GA1UdDgQWBBQ6uUyW1z0UqCTI3lUKVAVd
+XKLJmTAfBgNVHSMEGDAWgBSuiQGUQXdnve9/mE8p5xs6GLndUTA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYBBQUH
+AwIwDQYJKoZIhvcNAQELBQADggEBAEhXRkOpvru7XLHBQs1yIh40mI+uxY+Icj4B
+9J/QKu1rOjIi4J6+LH1cuAGY2EGXWIhpoRwtw5cAcUe4+ZFdZrLN6ew8GnC1eu6O
+iNPWwzLnEcKljbQBLIcGpciFBylsGiDuXspuQvCJ8+nR6M3WS++0/BC+rrhMDbCv
+PXJKFwNy1aqkML+Nb2afGfrgx+T+XDBTlY5qh1m7FGI6Gi8kh8MMjgnn5PE84wPn
+KShdtP2h94wAxY8zVgnfSPjTHs1KuGmsgWU9IPPSUt1E5/r4IrT77I65J6jZEmm8
+0G22RUHF1vwW0EfatKN18OL6SZ1fnGTuFx0agyZd4K37qHRwMAg=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b6:30:63:d8:b0:11:71:5f:03:38:e5:24:a7:88:
+                    9c:fe:f5:a6:2a:59:63:7b:18:39:d5:34:2f:27:4c:
+                    fe:18:27:eb:7e:71:25:4d:af:71:97:7f:f0:18:b0:
+                    19:a7:fd:ab:52:d9:01:aa:13:ff:3f:c9:c8:d4:87:
+                    fa:69:53:28:b7:52:4f:91:ac:55:cb:38:7f:61:32:
+                    b6:d9:20:f4:58:6f:c3:4c:4f:64:d7:14:34:8c:d3:
+                    ac:f5:97:8a:9d:f6:d0:0b:64:b4:3a:55:71:0b:92:
+                    b1:8e:df:2e:77:8a:fe:36:f6:0f:be:49:03:3d:42:
+                    fc:4c:e4:50:f6:3e:86:d0:e4:0b:15:cd:27:49:ae:
+                    7a:be:d7:05:28:68:f7:e7:35:1b:fc:2a:50:c1:66:
+                    f3:31:11:f3:f9:40:80:51:3a:60:9a:87:47:fc:46:
+                    99:e3:1a:c9:5c:76:d9:34:45:b0:82:d6:06:d7:ea:
+                    5d:13:ce:ca:4e:9d:2e:80:cd:b3:5c:47:11:dd:f1:
+                    8a:97:c7:8d:37:6a:1a:c7:97:13:ad:bf:9c:85:32:
+                    df:20:0a:a9:27:3b:e6:26:c6:9d:98:d3:d1:d7:a0:
+                    16:4d:b1:a3:3b:1f:19:c3:c5:81:dd:35:25:3c:86:
+                    8e:8b:76:69:f2:e5:35:5e:3c:6c:3f:7e:47:57:7f:
+                    eb:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                AE:89:01:94:41:77:67:BD:EF:7F:98:4F:29:E7:1B:3A:18:B9:DD:51
+            X509v3 Authority Key Identifier: 
+                keyid:AE:89:01:94:41:77:67:BD:EF:7F:98:4F:29:E7:1B:3A:18:B9:DD:51
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         3e:0f:33:42:25:43:7e:e0:36:64:99:cc:4d:38:94:6e:26:40:
+         50:7d:78:af:88:41:6b:44:4c:55:db:3e:11:2d:d3:67:94:79:
+         d6:7e:bc:e1:23:9b:2a:a4:a7:ad:3d:a9:fe:86:3e:3d:81:98:
+         7c:0f:21:60:a4:65:13:83:a3:c4:4d:12:d8:d5:52:3f:ad:de:
+         29:f5:ee:dc:31:ef:56:85:ce:7a:b4:05:f4:95:6e:ba:ce:ac:
+         09:19:49:eb:8e:ea:c6:dd:13:dd:15:b7:53:7b:44:67:ab:4d:
+         b7:41:c6:4e:de:f7:ca:bb:cc:7a:fb:84:ec:31:f6:ac:9e:26:
+         83:74:cf:4f:a9:6a:dd:dd:68:28:f7:13:2e:54:42:ea:39:8d:
+         44:51:3d:2e:05:11:63:81:0b:a8:82:96:72:ff:bb:45:a6:e7:
+         9b:f3:03:24:d0:21:e4:67:2b:a8:d9:61:aa:ab:9b:b9:f0:3f:
+         b7:16:fc:7b:32:dc:4a:33:e8:a3:d3:79:f5:fc:16:6e:95:23:
+         a5:ec:a7:75:76:ff:ff:8f:6b:c4:32:d2:4d:e7:45:2c:1d:7e:
+         8a:76:28:dd:e6:01:e1:f0:f9:45:5b:91:7c:0a:92:90:be:1b:
+         9c:0c:1f:b9:24:df:d2:f7:f5:fa:8c:76:cd:00:01:73:35:04:
+         a7:08:6a:dd
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALYwY9iwEXFfAzjlJKeI
+nP71pipZY3sYOdU0LydM/hgn635xJU2vcZd/8BiwGaf9q1LZAaoT/z/JyNSH+mlT
+KLdST5GsVcs4f2Eyttkg9Fhvw0xPZNcUNIzTrPWXip320AtktDpVcQuSsY7fLneK
+/jb2D75JAz1C/EzkUPY+htDkCxXNJ0muer7XBSho9+c1G/wqUMFm8zER8/lAgFE6
+YJqHR/xGmeMayVx22TRFsILWBtfqXRPOyk6dLoDNs1xHEd3xipfHjTdqGseXE62/
+nIUy3yAKqSc75ibGnZjT0degFk2xozsfGcPFgd01JTyGjot2afLlNV48bD9+R1d/
+6w0CAwEAAaOByzCByDAdBgNVHQ4EFgQUrokBlEF3Z73vf5hPKecbOhi53VEwHwYD
+VR0jBBgwFoAUrokBlEF3Z73vf5hPKecbOhi53VEwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA+DzNCJUN+
+4DZkmcxNOJRuJkBQfXiviEFrRExV2z4RLdNnlHnWfrzhI5sqpKetPan+hj49gZh8
+DyFgpGUTg6PETRLY1VI/rd4p9e7cMe9Whc56tAX0lW66zqwJGUnrjurG3RPdFbdT
+e0Rnq023QcZO3vfKu8x6+4TsMfasniaDdM9PqWrd3Wgo9xMuVELqOY1EUT0uBRFj
+gQuogpZy/7tFpueb8wMk0CHkZyuo2WGqq5u58D+3Fvx7MtxKM+ij03n1/BZulSOl
+7Kd1dv//j2vEMtJN50UsHX6Kdijd5gHh8PlFW5F8CpKQvhucDB+5JN/S9/X6jHbN
+AAFzNQSnCGrd
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+FAIL
+-----BEGIN VERIFY_RESULT-----
+RkFJTA==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
+
+----- Certificate i=1 (CN=Intermediate) -----
+ERROR: The extended key usage does not include server auth
+
+
+-----BEGIN ERRORS-----
+LS0tLS0gQ2VydGlmaWNhdGUgaT0xIChDTj1JbnRlcm1lZGlhdGUpIC0tLS0tCkVSUk9SOiBUaGUgZXh0ZW5kZWQga2V5IHVzYWdlIGRvZXMgbm90IGluY2x1ZGUgc2VydmVyIGF1dGgKCg==
+-----END ERRORS-----
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-ok.pem b/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-ok.pem
new file mode 100644
index 0000000..d3c332c
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/intermediate-restricts-eku-ok.pem
@@ -0,0 +1,291 @@
+[Created by: generate-intermediate-restricts-eku-ok.py]
+
+Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to serverAuth, and the target has serverAuth +
+clientAuth. Verification is expected to succeed as this is consistent with
+the requested key purpose.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b2:4d:55:36:b9:dd:25:6c:a3:e3:5c:2f:95:97:
+                    ac:3a:df:2d:6a:63:03:f7:e9:b2:a7:4d:f3:7d:21:
+                    78:af:80:cf:34:6a:47:ba:05:0d:90:ad:5d:5a:86:
+                    9d:c2:5b:7f:47:8c:0a:44:b6:de:d7:c1:17:e7:0f:
+                    44:ea:88:05:70:5d:81:95:81:44:24:b3:70:38:fc:
+                    ab:53:1e:41:75:0e:72:4d:3c:89:16:3e:b9:bf:e5:
+                    9d:5e:af:56:ef:50:a0:e0:da:bd:94:c0:39:07:52:
+                    b4:fb:3d:6a:4f:71:0e:4d:55:bb:69:4a:31:5b:4a:
+                    16:60:fa:fd:40:34:30:70:eb:12:d0:33:0a:9d:27:
+                    68:ba:2f:bf:51:7c:5f:fb:04:fd:c6:08:25:1c:44:
+                    a0:a8:4b:02:7c:fc:8c:ab:b4:e9:8c:c9:bc:ab:13:
+                    3c:1e:75:0d:09:cf:c9:56:db:2a:12:5c:e0:e1:58:
+                    70:95:df:99:9e:c9:21:b3:ba:3c:50:5a:26:a2:95:
+                    12:8b:9f:8e:f1:76:ea:85:94:ac:ef:14:44:b4:d9:
+                    44:28:a3:f1:60:c0:7b:e8:1f:01:bd:f7:78:bf:ef:
+                    cd:75:ee:dd:2d:e8:7d:5e:97:3c:b7:06:b0:16:6c:
+                    2d:0b:f2:07:7f:d9:43:f0:79:58:fe:53:41:c2:89:
+                    f6:9d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                D5:E6:09:2D:66:DD:8D:8F:23:BF:FA:0F:9A:19:29:A4:B0:3B:CA:25
+            X509v3 Authority Key Identifier: 
+                keyid:A8:E7:C3:7D:1D:87:34:60:3C:F6:5E:AD:96:99:05:CF:A2:06:2F:65
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         0e:b4:d0:41:86:b0:77:cb:da:ba:08:9c:cf:73:a1:4f:d0:0e:
+         62:29:a4:88:05:10:f1:e4:96:22:b5:80:47:56:5b:3e:52:69:
+         7b:ae:82:cb:cd:00:10:f0:ea:88:49:3e:2d:e7:8e:a8:47:7f:
+         af:af:69:2a:7e:69:12:55:48:40:88:31:04:0f:b6:85:69:db:
+         7a:ce:06:a4:7c:bd:f0:36:0b:3c:1c:19:d3:76:ec:77:51:cc:
+         ec:72:21:ed:d8:fb:44:2f:6d:fd:1f:ec:ef:6d:87:c9:87:e2:
+         97:6d:f8:7b:10:6d:1c:ad:be:07:7e:d4:b9:10:47:99:9e:f7:
+         bf:e6:13:c2:b3:55:5e:52:5e:62:a6:3d:4d:c2:5a:b6:f7:24:
+         dc:d3:19:eb:9b:52:7b:36:87:33:c1:eb:b7:da:94:d7:09:b4:
+         e8:9c:fd:19:b8:ab:ff:be:2d:20:06:11:52:ae:c9:30:12:43:
+         20:50:6c:b5:d0:cc:2e:85:ed:81:c8:cd:a7:be:f2:95:1c:c8:
+         36:ea:4e:37:a9:b0:41:68:e2:a7:46:b2:d5:f8:95:94:85:12:
+         98:a0:da:a9:12:8b:bf:2c:65:2c:48:b3:11:ca:69:b0:e3:80:
+         9b:36:c1:82:33:51:a2:8c:71:ec:9b:83:cf:4f:bf:23:d4:a5:
+         41:63:f5:40
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyTVU2
+ud0lbKPjXC+Vl6w63y1qYwP36bKnTfN9IXivgM80ake6BQ2QrV1ahp3CW39HjApE
+tt7XwRfnD0TqiAVwXYGVgUQks3A4/KtTHkF1DnJNPIkWPrm/5Z1er1bvUKDg2r2U
+wDkHUrT7PWpPcQ5NVbtpSjFbShZg+v1ANDBw6xLQMwqdJ2i6L79RfF/7BP3GCCUc
+RKCoSwJ8/IyrtOmMybyrEzwedQ0Jz8lW2yoSXODhWHCV35meySGzujxQWiailRKL
+n47xduqFlKzvFES02UQoo/FgwHvoHwG993i/78117t0t6H1elzy3BrAWbC0L8gd/
+2UPweVj+U0HCifadAgMBAAGjgekwgeYwHQYDVR0OBBYEFNXmCS1m3Y2PI7/6D5oZ
+KaSwO8olMB8GA1UdIwQYMBaAFKjnw30dhzRgPPZerZaZBc+iBi9lMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEADrTQQYawd8vaugicz3Oh
+T9AOYimkiAUQ8eSWIrWAR1ZbPlJpe66Cy80AEPDqiEk+LeeOqEd/r69pKn5pElVI
+QIgxBA+2hWnbes4GpHy98DYLPBwZ03bsd1HM7HIh7dj7RC9t/R/s722HyYfil234
+exBtHK2+B37UuRBHmZ73v+YTwrNVXlJeYqY9TcJatvck3NMZ65tSezaHM8Hrt9qU
+1wm06Jz9Gbir/74tIAYRUq7JMBJDIFBstdDMLoXtgcjNp77ylRzINupON6mwQWji
+p0ay1fiVlIUSmKDaqRKLvyxlLEizEcppsOOAmzbBgjNRooxx7JuDz0+/I9SlQWP1
+QA==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c7:a6:98:a9:bd:8e:27:ab:e7:f5:9e:36:a4:a4:
+                    f7:b7:59:0f:02:0a:5a:a7:0a:04:d9:d2:df:43:e4:
+                    13:61:c7:41:33:cf:1b:3e:e5:f6:74:36:6b:db:27:
+                    c5:cf:00:3d:c6:dd:2a:dd:1b:1b:ca:fd:d0:4b:a3:
+                    90:92:66:19:36:4b:bb:9b:dc:74:a6:fb:23:d2:8f:
+                    6e:74:35:1a:df:13:7a:40:df:a1:12:f3:09:a2:70:
+                    39:a0:e2:5c:0e:b6:9a:4c:53:f8:2e:12:fb:ea:db:
+                    9d:6a:6e:0e:41:2a:3d:b3:da:3e:7e:9b:2a:1a:2a:
+                    e5:70:1b:19:b2:10:d1:12:3d:e1:9c:f3:b0:05:40:
+                    79:c3:fb:44:41:80:01:10:2e:99:72:5f:f5:39:1e:
+                    5d:f4:2c:22:b5:c1:9b:ec:21:29:50:f9:36:3b:0e:
+                    8a:a9:0b:d2:e7:ce:74:16:10:74:4e:f7:f5:bc:14:
+                    ae:af:79:4a:82:f5:2a:e2:2a:e4:f1:e1:2c:e7:91:
+                    04:fd:a5:38:09:f6:21:ce:62:2e:65:0d:1f:30:3c:
+                    11:fe:e5:79:85:51:18:95:e6:9d:15:82:f5:22:d1:
+                    77:b5:4d:64:82:86:84:8c:59:90:86:b4:64:1e:a6:
+                    cc:20:d8:9b:09:b9:4f:7d:57:52:b6:00:9f:b6:d2:
+                    b3:ef
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                A8:E7:C3:7D:1D:87:34:60:3C:F6:5E:AD:96:99:05:CF:A2:06:2F:65
+            X509v3 Authority Key Identifier: 
+                keyid:A1:4F:77:20:47:E0:DD:E5:33:AF:6C:77:9A:CF:33:CA:99:39:B9:BB
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         86:0d:ee:57:98:f7:94:52:50:89:78:60:a9:5b:8c:f4:de:a9:
+         67:0d:af:b2:85:c7:73:97:c1:fe:62:a6:50:26:05:23:db:5d:
+         16:87:79:ae:10:d7:5c:cf:56:a3:ba:e3:ad:7c:7a:75:5d:0a:
+         00:66:d1:d2:25:1e:13:4e:87:96:11:a2:04:7c:77:90:35:8a:
+         21:28:71:82:62:1f:00:4e:d0:44:57:ac:0d:33:64:65:fd:27:
+         61:5c:53:ee:22:21:cf:1e:92:a0:4a:ad:aa:87:2e:c9:65:8f:
+         c3:ec:b1:6e:5e:82:a9:60:a0:7c:74:c6:93:6a:16:c0:76:32:
+         51:60:ab:83:1e:8a:ba:af:80:51:67:15:9f:6c:8b:65:0f:95:
+         44:60:fb:34:af:06:ab:48:2c:78:9f:6b:2f:fb:af:a8:cf:8e:
+         b4:b2:81:d4:e2:bc:bf:84:b9:2e:45:74:58:f9:b1:9d:b9:06:
+         b2:00:09:e9:8d:26:58:06:a7:09:c0:ba:bd:39:ea:83:2d:2c:
+         65:e1:44:19:67:f5:55:bc:81:eb:87:91:cf:1b:5b:24:6b:f3:
+         a9:2a:b4:b3:40:13:3a:2f:ee:53:04:09:de:a7:98:54:2b:77:
+         fc:78:14:8e:1d:6a:a7:db:18:a9:42:d4:a8:23:22:9a:d5:2a:
+         b9:d4:70:36
+-----BEGIN CERTIFICATE-----
+MIIDgjCCAmqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6aYqb2O
+J6vn9Z42pKT3t1kPAgpapwoE2dLfQ+QTYcdBM88bPuX2dDZr2yfFzwA9xt0q3Rsb
+yv3QS6OQkmYZNku7m9x0pvsj0o9udDUa3xN6QN+hEvMJonA5oOJcDraaTFP4LhL7
+6tudam4OQSo9s9o+fpsqGirlcBsZshDREj3hnPOwBUB5w/tEQYABEC6Zcl/1OR5d
+9CwitcGb7CEpUPk2Ow6KqQvS5850FhB0Tvf1vBSur3lKgvUq4irk8eEs55EE/aU4
+CfYhzmIuZQ0fMDwR/uV5hVEYleadFYL1ItF3tU1kgoaEjFmQhrRkHqbMINibCblP
+fVdStgCfttKz7wIDAQABo4HgMIHdMB0GA1UdDgQWBBSo58N9HYc0YDz2Xq2WmQXP
+ogYvZTAfBgNVHSMEGDAWgBShT3cgR+Dd5TOvbHeazzPKmTm5uzA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwDQYJKoZIhvcNAQELBQADggEBAIYN7leY95RSUIl4YKlbjPTeqWcNr7KFx3OX
+wf5iplAmBSPbXRaHea4Q11zPVqO64618enVdCgBm0dIlHhNOh5YRogR8d5A1iiEo
+cYJiHwBO0ERXrA0zZGX9J2FcU+4iIc8ekqBKraqHLsllj8PssW5egqlgoHx0xpNq
+FsB2MlFgq4MeirqvgFFnFZ9si2UPlURg+zSvBqtILHifay/7r6jPjrSygdTivL+E
+uS5FdFj5sZ25BrIACemNJlgGpwnAur056oMtLGXhRBln9VW8geuHkc8bWyRr86kq
+tLNAEzov7lMECd6nmFQrd/x4FI4daqfbGKlC1KgjIprVKrnUcDY=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b3:35:83:74:7b:e7:c4:56:bb:33:c3:b5:19:86:
+                    79:48:54:e0:02:be:10:8a:d4:74:9c:51:31:1a:7c:
+                    5c:bd:c7:29:4c:e7:65:2e:f4:41:7d:b9:02:10:38:
+                    9c:4d:dc:3e:47:bc:76:50:bc:12:16:ca:d6:97:e9:
+                    35:1c:88:f6:92:8a:66:2f:7f:8a:dd:8d:9b:2b:55:
+                    cd:5c:d3:18:b6:2f:3e:c2:a1:59:8f:cb:18:ad:c8:
+                    aa:a3:ae:d2:98:92:a0:50:44:f0:7b:13:73:47:69:
+                    7d:f3:1a:49:37:29:9f:4a:40:1f:1b:28:00:82:f8:
+                    9d:80:02:fd:e6:37:d8:a9:6b:5a:3b:e2:ce:d8:a0:
+                    40:7a:27:30:4c:eb:0a:42:a6:1a:bc:20:bf:3f:3a:
+                    b0:ab:ee:38:7b:c9:07:c2:6b:87:54:cb:9e:1c:60:
+                    36:a5:dc:01:d1:44:0d:e2:ff:23:1d:47:d7:17:89:
+                    1e:38:fa:09:30:f4:19:a0:be:60:4a:a9:f8:62:4a:
+                    bf:f1:ca:01:33:17:f0:78:f3:fa:19:58:11:cb:ef:
+                    f5:20:2e:91:80:85:11:ec:ac:55:51:9f:64:6a:87:
+                    68:0d:9f:d7:93:0a:7a:8a:1c:7d:67:10:73:91:f2:
+                    0c:81:c6:e8:93:5a:d7:b1:65:2c:e9:54:33:5e:39:
+                    b7:9d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                A1:4F:77:20:47:E0:DD:E5:33:AF:6C:77:9A:CF:33:CA:99:39:B9:BB
+            X509v3 Authority Key Identifier: 
+                keyid:A1:4F:77:20:47:E0:DD:E5:33:AF:6C:77:9A:CF:33:CA:99:39:B9:BB
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         64:60:7d:50:48:0f:3b:0d:00:93:1f:a8:95:5b:0b:55:7e:1c:
+         bd:76:cd:f8:26:49:ec:e5:e2:16:08:29:ef:cd:0d:57:c0:f7:
+         26:a3:80:cd:0d:61:8d:31:98:25:f3:8c:9a:e0:0d:d7:eb:bb:
+         aa:94:13:99:7c:8c:fc:ff:ea:b6:bc:66:36:9f:d8:50:ee:e1:
+         f6:75:a9:e0:30:12:38:70:a6:ca:72:15:46:d0:70:0c:92:f7:
+         8d:5e:ab:32:ef:76:cd:33:ed:a9:b2:1e:7c:da:ad:c3:43:70:
+         2b:6a:20:aa:47:89:9a:05:31:bc:13:e4:8e:56:1c:99:27:85:
+         2b:98:08:ec:54:3f:e9:4b:50:b0:b9:d3:86:2f:c3:8f:a3:61:
+         d4:0a:39:ae:55:0f:d1:57:22:05:53:46:88:68:92:22:f7:6b:
+         e4:62:98:ad:7b:37:e4:ce:5e:ac:97:11:93:69:ad:d3:3c:db:
+         ed:dc:e9:3e:82:14:0c:1f:55:79:5f:78:82:0f:c8:72:2a:19:
+         3b:92:a7:80:bd:4d:c7:d7:d2:be:36:e9:d2:56:17:e7:8e:71:
+         25:4c:97:03:62:78:45:f4:ba:6a:e5:61:e9:a6:13:26:3d:f9:
+         14:90:7f:2d:83:70:bd:58:20:a9:40:a0:a5:81:55:40:d0:ce:
+         f0:81:b5:23
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALM1g3R758RWuzPDtRmG
+eUhU4AK+EIrUdJxRMRp8XL3HKUznZS70QX25AhA4nE3cPke8dlC8EhbK1pfpNRyI
+9pKKZi9/it2NmytVzVzTGLYvPsKhWY/LGK3IqqOu0piSoFBE8HsTc0dpffMaSTcp
+n0pAHxsoAIL4nYAC/eY32KlrWjviztigQHonMEzrCkKmGrwgvz86sKvuOHvJB8Jr
+h1TLnhxgNqXcAdFEDeL/Ix1H1xeJHjj6CTD0GaC+YEqp+GJKv/HKATMX8Hjz+hlY
+Ecvv9SAukYCFEeysVVGfZGqHaA2f15MKeoocfWcQc5HyDIHG6JNa17FlLOlUM145
+t50CAwEAAaOByzCByDAdBgNVHQ4EFgQUoU93IEfg3eUzr2x3ms8zypk5ubswHwYD
+VR0jBBgwFoAUoU93IEfg3eUzr2x3ms8zypk5ubswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBkYH1QSA87
+DQCTH6iVWwtVfhy9ds34Jkns5eIWCCnvzQ1XwPcmo4DNDWGNMZgl84ya4A3X67uq
+lBOZfIz8/+q2vGY2n9hQ7uH2dangMBI4cKbKchVG0HAMkveNXqsy73bNM+2psh58
+2q3DQ3AraiCqR4maBTG8E+SOVhyZJ4UrmAjsVD/pS1CwudOGL8OPo2HUCjmuVQ/R
+VyIFU0aIaJIi92vkYpitezfkzl6slxGTaa3TPNvt3Ok+ghQMH1V5X3iCD8hyKhk7
+kqeAvU3H19K+NunSVhfnjnElTJcDYnhF9Lpq5WHpphMmPfkUkH8tg3C9WCCpQKCl
+gVVA0M7wgbUj
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+SUCCESS
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-sets-eku-any.pem b/net/data/verify_certificate_chain_unittest/intermediate-sets-eku-any.pem
new file mode 100644
index 0000000..bdf50fd
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/intermediate-sets-eku-any.pem
@@ -0,0 +1,291 @@
+[Created by: generate-intermediate-sets-eku-any.py]
+
+Certificate chain with 1 intermediate and a trusted root. The intermediate
+restricts the EKU to clientAuth + any, and the target has serverAuth +
+clientAuth. Verification is expected to succeed because intermediate will match
+the "any".
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ae:2e:8b:18:8d:f7:76:2c:94:0c:3f:a0:b6:ea:
+                    70:1f:5e:c8:48:c5:aa:ad:55:6b:bd:55:68:0d:8e:
+                    ce:e5:99:27:c5:2c:b2:9a:29:a9:8f:8e:c3:c6:97:
+                    89:6d:31:d7:a4:8f:d8:36:37:4f:33:c7:d6:42:03:
+                    11:08:c4:7f:35:8c:ee:0f:1b:7a:31:74:04:aa:01:
+                    d3:1e:8b:5b:01:9d:60:4b:9c:d1:8f:1e:ab:e5:dc:
+                    8f:17:77:49:e3:f6:d5:82:a5:2f:0a:e8:dc:9f:96:
+                    1e:2a:a1:41:d1:67:2c:9e:f3:7f:94:0c:6e:cf:5f:
+                    55:52:37:05:d0:39:37:1a:6e:11:ed:db:fa:aa:92:
+                    a7:4f:50:29:07:69:af:1d:a7:99:fa:e1:56:f0:03:
+                    38:b0:ae:6b:e7:19:0b:dd:c3:07:31:8e:84:04:a5:
+                    b4:eb:b8:bc:23:f3:40:b0:17:b4:ab:9e:3f:05:96:
+                    89:fc:84:23:cc:d1:06:c2:e4:8b:c6:65:f5:24:eb:
+                    72:31:bc:41:7d:3a:c9:55:08:0c:ee:a6:ae:1f:78:
+                    17:f8:a7:9d:7b:b1:82:f5:ce:82:6b:a8:b2:c6:8a:
+                    b9:be:a5:d8:39:f4:49:e2:4c:53:32:85:26:53:4d:
+                    44:ce:d5:3b:a0:6b:e7:d9:02:a1:5a:ef:e1:a5:81:
+                    a7:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                EB:B0:1C:BD:B7:68:B8:D1:B9:8A:C2:9F:5D:CF:DD:AF:F2:62:70:8A
+            X509v3 Authority Key Identifier: 
+                keyid:EE:C6:9A:65:CC:FB:CE:A0:3E:17:02:F9:68:12:86:B6:22:09:60:B4
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         04:83:63:1b:67:f1:17:c4:b4:65:6c:fa:2f:b8:d0:67:b4:14:
+         b2:51:4f:a2:cd:c5:20:a0:83:b2:f6:20:af:49:07:59:89:8e:
+         cf:bc:59:3a:b6:c3:e0:ec:a7:cd:87:7c:a6:72:bb:0b:6c:97:
+         16:00:d2:2e:0b:66:c0:35:66:5b:f9:d8:25:cb:f2:20:f2:39:
+         b8:a9:a6:ab:47:ba:06:ba:97:a5:27:f7:9b:2a:68:35:e0:96:
+         39:37:16:2f:28:b9:ad:bb:49:59:18:fe:d1:7b:5d:5e:ec:1d:
+         f0:e7:77:61:ab:f4:c6:6d:25:fa:a6:56:0c:1c:aa:6c:37:97:
+         e4:4a:9d:56:16:d0:e2:45:05:e9:d2:72:8a:ad:d9:3d:98:ad:
+         7f:d3:a1:1a:f3:e4:f6:eb:c2:b1:97:49:42:55:45:ab:a2:03:
+         22:24:2f:7f:c6:ed:7b:87:47:ab:3f:6e:a1:d9:c5:6c:21:db:
+         73:de:47:e6:62:4e:7b:53:c0:df:6e:38:1d:2d:37:29:0a:81:
+         46:7f:2e:0a:bd:c5:4d:9b:71:86:3e:22:81:bd:2a:79:9e:e0:
+         2f:44:aa:bc:f8:a7:12:f3:79:8d:ec:69:0c:10:ed:7f:df:a2:
+         a2:07:88:2c:1d:a9:1b:61:fd:b8:59:3e:70:4c:7d:f1:95:61:
+         d4:98:61:ff
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuLosY
+jfd2LJQMP6C26nAfXshIxaqtVWu9VWgNjs7lmSfFLLKaKamPjsPGl4ltMdekj9g2
+N08zx9ZCAxEIxH81jO4PG3oxdASqAdMei1sBnWBLnNGPHqvl3I8Xd0nj9tWCpS8K
+6Nyflh4qoUHRZyye83+UDG7PX1VSNwXQOTcabhHt2/qqkqdPUCkHaa8dp5n64Vbw
+AziwrmvnGQvdwwcxjoQEpbTruLwj80CwF7Srnj8Flon8hCPM0QbC5IvGZfUk63Ix
+vEF9OslVCAzupq4feBf4p517sYL1zoJrqLLGirm+pdg59EniTFMyhSZTTUTO1Tug
+a+fZAqFa7+Glgaf7AgMBAAGjgekwgeYwHQYDVR0OBBYEFOuwHL23aLjRuYrCn13P
+3a/yYnCKMB8GA1UdIwQYMBaAFO7GmmXM+86gPhcC+WgShrYiCWC0MD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEABINjG2fxF8S0ZWz6L7jQ
+Z7QUslFPos3FIKCDsvYgr0kHWYmOz7xZOrbD4OynzYd8pnK7C2yXFgDSLgtmwDVm
+W/nYJcvyIPI5uKmmq0e6BrqXpSf3mypoNeCWOTcWLyi5rbtJWRj+0XtdXuwd8Od3
+Yav0xm0l+qZWDByqbDeX5EqdVhbQ4kUF6dJyiq3ZPZitf9OhGvPk9uvCsZdJQlVF
+q6IDIiQvf8bte4dHqz9uodnFbCHbc95H5mJOe1PA3244HS03KQqBRn8uCr3FTZtx
+hj4igb0qeZ7gL0SqvPinEvN5jexpDBDtf9+iogeILB2pG2H9uFk+cEx98ZVh1Jhh
+/w==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:d1:41:40:6f:cb:25:05:d9:29:d0:a3:c7:fe:2f:
+                    f0:53:ad:46:36:19:aa:b1:1f:3f:7a:a2:e0:fb:03:
+                    2b:77:65:6a:79:eb:f3:a3:16:13:34:83:3b:42:de:
+                    a2:bb:e2:bf:d8:d2:75:3d:48:38:86:bb:2a:7d:14:
+                    a3:88:f7:7c:00:f4:0a:6b:6b:aa:9b:44:24:62:fe:
+                    db:a3:42:55:15:67:2a:32:ff:b2:4d:80:93:d0:84:
+                    ef:1b:dc:7c:ac:56:2d:54:08:02:f6:18:6e:b5:80:
+                    a8:77:52:1f:b8:2c:09:6d:cc:f8:1c:04:91:62:6e:
+                    1e:dd:1d:89:b2:f1:23:0b:4d:4c:6c:da:49:3d:61:
+                    83:72:0f:66:36:12:3f:f3:ff:53:52:73:53:a1:ca:
+                    38:bd:c3:48:bf:7a:2f:13:19:d7:c2:28:e1:6f:32:
+                    00:5e:64:ac:4b:05:7a:77:62:57:55:a9:59:83:d5:
+                    ed:a3:2e:28:34:71:79:2f:b9:c3:9e:df:b3:2a:b1:
+                    59:cd:04:00:1d:8b:11:56:ae:c6:67:f6:4f:1d:58:
+                    07:65:e0:b0:2f:ef:57:6d:de:c1:a0:7c:6e:38:a8:
+                    45:26:21:96:e0:f6:ef:0e:28:cf:01:70:57:dc:20:
+                    15:08:ad:e8:e3:98:74:8c:54:32:c1:28:17:e0:de:
+                    a1:8b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                EE:C6:9A:65:CC:FB:CE:A0:3E:17:02:F9:68:12:86:B6:22:09:60:B4
+            X509v3 Authority Key Identifier: 
+                keyid:42:75:41:34:C5:59:9F:99:A3:9B:1C:0C:57:DB:5C:C7:C1:48:B7:91
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication, Any Extended Key Usage
+    Signature Algorithm: sha256WithRSAEncryption
+         7e:65:1a:1b:ca:f6:29:2d:fa:64:43:07:29:94:06:40:45:c5:
+         86:64:c8:7e:3d:83:78:1b:a2:41:2c:35:33:80:f1:d3:77:84:
+         6c:7b:db:f1:65:3b:71:72:85:67:5e:a9:b3:91:8e:39:41:ae:
+         ba:ad:3a:c5:7f:f2:be:e3:af:83:b1:3e:26:c0:36:f4:c7:12:
+         21:51:2f:1f:37:19:a5:03:25:d9:25:9c:b1:10:1b:56:06:1e:
+         7a:08:b6:d8:04:de:3c:23:8b:cb:22:ea:24:65:a8:89:f4:61:
+         8b:16:c4:9d:bb:31:f1:20:56:18:32:eb:26:43:7a:83:71:33:
+         e6:ab:d2:3d:44:d2:20:2c:39:b7:79:46:59:41:8c:f4:b2:16:
+         a1:23:2b:cb:67:10:e3:c0:2d:e5:a5:57:8d:a9:86:f3:d6:79:
+         54:60:34:d7:48:f4:34:84:a2:55:0a:7c:7c:3e:66:8b:6c:6d:
+         a1:5a:cf:fd:96:f1:f9:f0:88:f8:f5:66:b1:7d:c4:cb:8a:47:
+         7d:d3:d6:fa:3c:34:76:8d:9d:1a:68:bc:d7:cd:2a:eb:99:cf:
+         d2:f0:bd:44:3e:91:77:45:85:56:a0:80:74:a7:1d:65:a6:d5:
+         ef:82:95:80:d3:f9:a0:39:e7:34:2d:4e:63:cb:47:f8:05:13:
+         34:62:ec:c8
+-----BEGIN CERTIFICATE-----
+MIIDiDCCAnCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0UFAb8sl
+Bdkp0KPH/i/wU61GNhmqsR8/eqLg+wMrd2VqeevzoxYTNIM7Qt6iu+K/2NJ1PUg4
+hrsqfRSjiPd8APQKa2uqm0QkYv7bo0JVFWcqMv+yTYCT0ITvG9x8rFYtVAgC9hhu
+tYCod1IfuCwJbcz4HASRYm4e3R2JsvEjC01MbNpJPWGDcg9mNhI/8/9TUnNToco4
+vcNIv3ovExnXwijhbzIAXmSsSwV6d2JXValZg9Xtoy4oNHF5L7nDnt+zKrFZzQQA
+HYsRVq7GZ/ZPHVgHZeCwL+9Xbd7BoHxuOKhFJiGW4PbvDijPAXBX3CAVCK3o45h0
+jFQywSgX4N6hiwIDAQABo4HmMIHjMB0GA1UdDgQWBBTuxpplzPvOoD4XAvloEoa2
+IglgtDAfBgNVHSMEGDAWgBRCdUE0xVmfmaObHAxX21zHwUi3kTA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0lBBIwEAYIKwYBBQUH
+AwIGBFUdJQAwDQYJKoZIhvcNAQELBQADggEBAH5lGhvK9ikt+mRDBymUBkBFxYZk
+yH49g3gbokEsNTOA8dN3hGx72/FlO3FyhWdeqbORjjlBrrqtOsV/8r7jr4OxPibA
+NvTHEiFRLx83GaUDJdklnLEQG1YGHnoIttgE3jwji8si6iRlqIn0YYsWxJ27MfEg
+Vhgy6yZDeoNxM+ar0j1E0iAsObd5RllBjPSyFqEjK8tnEOPALeWlV42phvPWeVRg
+NNdI9DSEolUKfHw+ZotsbaFaz/2W8fnwiPj1ZrF9xMuKR33T1vo8NHaNnRpovNfN
+KuuZz9LwvUQ+kXdFhVaggHSnHWWm1e+ClYDT+aA55zQtTmPLR/gFEzRi7Mg=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c3:96:66:c7:e7:fd:21:14:ec:df:4a:05:1a:8c:
+                    22:da:8f:3e:b7:8e:ca:a2:de:d7:e3:08:05:cd:28:
+                    1c:da:d4:99:ba:ad:de:92:07:44:18:55:e7:b5:41:
+                    6b:38:64:18:06:ab:6c:b8:ad:3d:b8:4e:c8:fa:8c:
+                    fc:58:2c:2c:a8:42:08:28:b4:85:2a:aa:57:e2:a8:
+                    76:4a:6e:fe:38:2f:d1:14:c6:52:6f:05:a4:89:54:
+                    c2:0f:f0:93:83:09:b7:55:56:94:7b:57:65:87:09:
+                    dd:61:ea:1a:02:3c:24:a5:cc:2d:d3:7c:0a:dc:2e:
+                    67:a2:7f:91:ad:b4:76:76:02:ac:7f:85:5f:61:86:
+                    0c:60:15:a0:82:7f:85:16:f4:10:8d:49:27:e4:33:
+                    58:75:55:6b:5a:ab:c7:d1:bd:3d:a8:3b:68:1b:b4:
+                    de:68:89:c4:87:fe:87:04:d4:52:f3:8f:fa:2e:44:
+                    79:c1:62:46:b7:88:4c:bb:75:61:fd:e6:c5:6a:fb:
+                    a8:3b:ef:a7:e6:1a:1e:44:2d:61:a7:4e:63:5e:66:
+                    b8:f7:85:60:74:8b:ea:20:82:84:84:71:f5:1d:c6:
+                    0c:c2:ee:11:78:01:ae:44:5a:e3:7b:97:2e:01:d0:
+                    18:91:77:01:23:7f:d2:21:73:f4:f3:9a:94:ad:93:
+                    2e:a1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                42:75:41:34:C5:59:9F:99:A3:9B:1C:0C:57:DB:5C:C7:C1:48:B7:91
+            X509v3 Authority Key Identifier: 
+                keyid:42:75:41:34:C5:59:9F:99:A3:9B:1C:0C:57:DB:5C:C7:C1:48:B7:91
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         47:b6:e9:3a:86:dc:51:de:1f:9c:e4:ce:08:aa:28:ee:50:9e:
+         92:7f:f9:ee:0c:44:1e:e8:e3:18:6d:51:db:e5:62:56:af:fd:
+         ca:37:a2:7c:a7:a9:64:8d:27:52:0c:c5:4a:64:6f:16:30:2a:
+         c6:63:18:7f:7c:3d:fe:a1:5e:4a:04:87:1b:64:1c:c6:03:89:
+         d2:a2:3b:e0:bf:f4:a4:b3:93:ef:75:fd:3b:4e:1f:9c:fd:36:
+         ec:3c:6f:ec:b5:62:21:e0:c4:4b:07:71:34:29:61:20:b9:50:
+         6c:fc:b9:a1:74:39:93:10:32:ea:1e:11:5e:20:27:47:7a:6a:
+         3d:52:e1:35:85:9d:ca:65:f2:0d:b6:a2:6a:34:af:31:23:a1:
+         ca:be:cf:dc:ab:36:04:4f:d6:93:9b:c1:3f:fd:dd:34:d4:ca:
+         16:84:4b:92:4c:d5:11:4d:c3:ae:11:a7:8d:c0:cf:c8:94:27:
+         5b:7f:9d:e0:d8:fa:67:7a:75:ce:a4:e7:d5:d0:c8:fc:ce:31:
+         20:ab:7a:3b:59:8f:41:ce:58:7f:70:96:9b:e3:00:3b:ca:9e:
+         f3:df:66:e1:86:7a:f0:1c:72:6b:96:2a:29:c7:3a:f5:c3:c0:
+         c4:5f:a6:86:7f:c1:f5:ae:6f:4c:14:3a:b1:15:5e:c9:c8:77:
+         52:d1:da:c5
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOWZsfn/SEU7N9KBRqM
+ItqPPreOyqLe1+MIBc0oHNrUmbqt3pIHRBhV57VBazhkGAarbLitPbhOyPqM/Fgs
+LKhCCCi0hSqqV+Kodkpu/jgv0RTGUm8FpIlUwg/wk4MJt1VWlHtXZYcJ3WHqGgI8
+JKXMLdN8CtwuZ6J/ka20dnYCrH+FX2GGDGAVoIJ/hRb0EI1JJ+QzWHVVa1qrx9G9
+Pag7aBu03miJxIf+hwTUUvOP+i5EecFiRreITLt1Yf3mxWr7qDvvp+YaHkQtYadO
+Y15muPeFYHSL6iCChIRx9R3GDMLuEXgBrkRa43uXLgHQGJF3ASN/0iFz9POalK2T
+LqECAwEAAaOByzCByDAdBgNVHQ4EFgQUQnVBNMVZn5mjmxwMV9tcx8FIt5EwHwYD
+VR0jBBgwFoAUQnVBNMVZn5mjmxwMV9tcx8FIt5EwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBHtuk6htxR
+3h+c5M4IqijuUJ6Sf/nuDEQe6OMYbVHb5WJWr/3KN6J8p6lkjSdSDMVKZG8WMCrG
+Yxh/fD3+oV5KBIcbZBzGA4nSojvgv/Sks5Pvdf07Th+c/TbsPG/stWIh4MRLB3E0
+KWEguVBs/LmhdDmTEDLqHhFeICdHemo9UuE1hZ3KZfINtqJqNK8xI6HKvs/cqzYE
+T9aTm8E//d001MoWhEuSTNURTcOuEaeNwM/IlCdbf53g2PpnenXOpOfV0Mj8zjEg
+q3o7WY9Bzlh/cJab4wA7yp7z32bhhnrwHHJrliopxzr1w8DEX6aGf8H1rm9MFDqx
+FV7JyHdS0drF
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+SUCCESS
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Intermediate.key
new file mode 100644
index 0000000..4118dab
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAtgrzC0Mq2OUV9kiUIekjYQ8OzFqmukWwi5lURAtWTrET4s65
+WArcd0E2hqzrtFQEdx/P9h0SxVg4Zf8gQfBDnaS/+mE7dVLwOZ1b++KI+2kysrlN
+qDSI487hL/QD1Rs/GquVmKFvhysQdgJPumd/miNckIrcsyelKBSYHrIGkgpgN/5W
+thaEWQGl6R0E1UZm5DD6Dg7C12YhSvyZT4UzljZ93FwEFrxc7vNtTbaiDzn84mOW
+vz1aYQKP29cHxCQC8AJS5ywIeLmN2V8tzGwenvmRlei+E3cCs4aryyTtSr82KS1m
+Nh78PTrFDyNd6S5B2HmX7ovMdS3HOr5N5f2iMwIDAQABAoIBAQCjf7AMZHYeOZqB
+CWyqDEdvS+PhnGV0lVTb/IlXV8tbf+U5ZqvGrHKYVSTQzowf+PnNt+/Cvxf10KdO
+zFY/grhn5OlbympplGUXigJsd2jKTRBwvsdFlHGCEXPk6sdqZj9GB+ejHxMZFo+B
+PdA3ve84FBx14ZIZAjluhxidMQ1/qXgisxdfDB90OcR+3KKAwsPNzNqmlWf0H2Wo
+ntCB2qlgihzcF+WnmZRonDED7GZ2PlBO+CuDFVX5wFoHD0Qa+Q9Megk+WRofPnfx
+i/Y4P9wf1LJh0MUibA2CSIUxNgeiFETMmrc4D6483sZ80CfjKhZkAwaOpaLeAO/V
+bjT2oF0JAoGBANzUmwLwGoT2YvaeyYyfrso6HH+qYiDk2eQraV+YudLMUwMv00K2
+LpEiqTM0E7lgL0R1C5YlzAj23JihAdWQbCK0Cc4bNrtl5uxO9eliybX7g1AN3vGK
+FfsZ8Apzeq1S0AHp/ujdx8xQJRQffBkz/cVz6km+1V+ngoXxI1nuVmctAoGBANMI
+8d0+Q7bWrHozbl9QCM10t2gue6dSH/zFpfutlh3NCQmTYRrDxHqZ9mAcpgjKk+xU
+YImsRoV3QWW1CCWIHCjF0XJMOoEDzoHTdhXe4FGCuvxl7lM6Ip915gPA7bCQsnjT
+dSgcHx/U4ZcWC6vxrJstHS76TIKllCBkh/v6ugrfAoGAMWlA5f9jb6zRp+0Pm+9+
+5LDr/4TYQBP0zSEJiuJZcdlWGbhxxNOOx9rBkTttzmS/LsohtY7vgHH8GgqspmSS
+TZGLQBrGvFeou+dPDtGT4KxOTXNNFy6WjIDZPP1pza5h7Iu3BdPaCNVF9qK02c7Q
+UYYGPkn85FJO2jmCd3Tk0GECgYEAyWxILbs4NlM/3IMqXZ6uSCuWwsFbFKC2FzUu
+78EG/vqsHY+lpz4jUkGRfrrZXpq8BV2jpsPfq5Pfv4vCctAu0Fu/RTCsxmczu4Zx
+tv47bJEyl6VSi+15DmohWIPUWipBrkFmGRp+Ooudpe9MxJiu/o1YxXXxgOE5OJG7
+mS3+VFECgYAWZ4DZUS8jTIRwmRkCgNOfWPUCvY6UL7JmnwhJvBnPBR4pA2Y48Uii
+V428PV8LjtWiaS6iz80chAz+DQSm639My5VX7wDhlSh2fl3JWaMk0pCwFn7PCC2C
+mCLSXcny7p0KfbTGEMtvQ5PyMM5hipY6aePKxEB3OKpdot++KahXPQ==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Root.key b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Root.key
new file mode 100644
index 0000000..f1ce6e39
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA2eIIrEZPPsksDhsuDcsFLrJg3Tk7MZA8ie5vMj9OnEqT15fl
+6Z0KcqV3yOZn2+Di1zWr1XsmKpc5TATjMpPfaZ5ex/s6U3AYkTl2I6plW+CHMsss
+bG/nOJ952yPqPIab8gPT3xVczli1RnddIQn5464WzubVlUHV7sd0ib/cyIBH4Elu
+/yZrCtTCBCGgtbAHTRsc4ahTIxM/ATHZP9wtcIthSbFtb8ZO9zVFF0A5nSgdd2iC
+wnWawp+Qq0zJij5oKiy6YqvhWMg7/sCVjlUzU8o/+5qulRNlP6aa1pjxrXJ0zthl
+Ep9j/WPFP5A92LIr/kj62qvyScYdKrr4c+FQoQIDAQABAoIBADZsYWI2xcCXG2wC
+mgRT+91oLQ3VJ9X/gz+o0Rmp+obLkhY+GO27jkqIzlzeQH8Js1+yRlOyyRvGXkeA
+yQdvLFUDfAVT2YPR8jEEO2A5RcwnT13+pgAIC6d3HkcAt5eawAU5s4h7m4znV5Bc
+bGS+Sy714ziXLC52HWvhYliuFgX2nv+3BRCvN1kntFwezoxpWo1+s01n/sjLwfoW
+JdWco+rdI+XMQf4XPlS19bNP8fzfqyiCfblynlj96rlOvRJassn10e4PkWWX/lhA
+45acCzl5ob2HHPOcVesNYm8JGZCTSaRh4eQbvPwCJnzBzlVPhl2ZaMjdvGFoA/Ym
+saSo32ECgYEA7nUHhZFdO8blZipnBpMB928BNM+RdJ1CVaurSfhGMMOI4Gw/3Xh2
+kpd7Ctw1jVyiPJEO43OVE8OSxU/TVxB7/EupcTSGDpWUAtwFmFGVV2iglCCv+bfO
+T01mFJaUh0r/x4SPtgWsbg2qQowbsWymewyVgp7li9V/zAzth2R5lp0CgYEA6emF
+dRWEwColgaKx5Y5N/v8EGBwr4lt9e/469w8/w7KHeuhjVbErcr/OFO3LMSpSZYKl
+NuhItpsF2vn+RgOpgdivk07W+6+CWnkI7uuflYWwDGNlJbsQdFFYHXdqZkraGwuc
+4F/Gj9poTvSBmuq/+a0dXdznkLjzwZW9DkxjANUCgYBDsN0NwZIh4fhE5EySxTCN
+uOkywKmd3PZzs2PQf7YNKbS1YIVQrV2bh3zaPGDHXECByPvc/XaYQbBsapin4noP
+yLq09CAKC9QUa7j64E3wnjbXVeAel1rtkB8mQQiwz02EqdDR7xJfu2lVssg5uYoY
+7B2VEt4+r8CzgAf9ozwQ0QKBgBTxn813+8bo/yq4Uo1nTHyEgO1XuGYwIvAzNzxf
+1SnElnTe0eYeUvGXUvB0yjjAvVfxdrGP4tLW1pNgxu+l7EuAB1h9OqqWcGmBZNdf
+qY5NxhGV5LyYH0UK84TP6Gr4UMcTJxy/9ptyxtwq8F/anQ3R/kslOGaw6MJwkdjx
+5ftxAoGANxbKqR7qdhOrsR+oeESl6xBTFVxYv0lQPul70rjV4/GHQ5jY4+N61FFj
+5K5MHWEngoBVmIwVDNmo5hAgdDssslzMYp22d05alkgkcear7pk5dZd5BcYV+AmY
+qbAXbA6Gn/s/+juYQQiTbR55xxeQr3W3V/D38PVi18++TYKmFzs=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Target.key b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Target.key
new file mode 100644
index 0000000..37b7714
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/constrained-root-bad-eku/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA8AgA6l2T+589/bWub4kCuX9LdblRy+9v3XtQtiqn+pxBiG6h
+viuLVCoCyMAs7citdZ2EIsUS2GOsYIVCPeLFWQABx01jCL+iY8/d/EjmVeYsXNa/
+4dEZCVaLQ/K+ugSBM31c7iY798IV1VcRTAj8SOT1i9Fiy3IQfv6uhP/41jUggPO5
+WaN/Hb9v9W1rKeSxXi4gzIAE+G1nBBhxrMPPU0vKGqEGwX3X/iSoa9JSGEp6rcQv
+cOGoZpqU3BOyJk3gYPFnVzHxANWyPDFqNFJ1K9Lz07DW91S+nLqZOYJQAu622MS3
+zggwp44tsGt48RknzcXDpPLHkbNeYZTmp5Q7xwIDAQABAoIBAQDYFAxm7sjJ6keO
+/p/CTk/Cl2Jh3utQsAI/GsTEyM+hHkssO5OlM2Pg51TMTZvkjxP/3kXWWxyk6c2C
+slO4jE1l2ki/lt3BQWo5eiX299VKnMNw6+2epnJoYp1KFNs3b9izdnZS+N5hIjov
+bIrorOoX4O1e6aaLSOqquDB5EEl84mY/pdUvbDaIParYrTUAw8Rpc+InNYB4jMGP
+M1uWWkVzfRwg7BdwGYC5g7Dd/1vAlT9PUwug8fDpkhCM1+qaMzaWIvt1WMyFVzkt
+7tIhgd3FVQvHtb8XPLNvMnaSfoqfjiQpw/YUR9Cg49XleCP7oU32gVhp0370gvZL
+jBnLW2sxAoGBAPtavP2uay8uBICyZkBCpV74JmnFyA5sjCkKJyK4sxsb7T7uxK0z
+z60dfrvVN318QiNnpAaqFfpdJPOMxTkSAOMVsDsOeterHXXdxK36pq+Ppsv3Mr07
+s2H7wzijXwOXiRSMIjKvsvI8yQMjeL6KGKwTyvMbhz6xXwRuvlaGhN5PAoGBAPR3
+sNAeTpOv0lF1+ovWP1IzzFkBBLUmaTKStpvMLyqBB3mO8PASJvMyYSwTmDcNoyR4
+7ouHNxNjzUOfWHAsvFAISh4qzLVyHdTolcH/1FRJWvDFloW9eJZ3ah/LLgh/XxMH
+JpAI7ZtOxGzVvhMDpDzpuhzzDdSc2vD30ECYCSUJAoGBAMg43qVBhDYf9QxDj/oO
+XMcK6yaEmzkKNJgYc1ZQRLQKifEAqADEbeEbxrF/ReWazuB/FYUNhdZazi2H7C7q
+7XP2+dwBS89yilQypRJlwNPGnXqKXouo+9rIfxkl4nDtDBAnFLLFjMkzrHfokK1P
+OhoQ5jDDs/c3uVXBlbM9Eqg3AoGBANYZjOx33nFRZmyZWpflRweC/dBlJ+5OeYsl
+GnBP3m7nGdjH6xMVkq8izB+B+akddGeY0bG49e8xVK+IejejtjiRSfgpm6a9DotM
+Nvv0Rcybgd2j7jOqwFVmt7aohuRCSM2tUYSLjSybS0PV3eVhJxGB0NFRvCccSFMJ
+dYVcoYWhAoGAZTI9EAk+bdmt59VzagxQlPw8lCO9Xr7k1fLsy/NBGS6WK1DonMPc
+K3eofw5F43kAef5RGewWA1lkrMm0uKt85mrBc4VDEMI+Rziwvb5sOA/mtBvJ1N42
+JIQ5DewZVQujBRdoWk+JOEtI5s97kHURj93EJiuHzSKOoFw1pjhU63g=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Intermediate.key
new file mode 100644
index 0000000..33a66f3
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvZoIZ3KlTbo5xArVqUJGeqDz8isfg5FYpwA7sxdR5R+DE0QQ
+FH+EbZdX3jIAvRUY5MeJi25bQVGt08n3PnVRdFxxQC6blb6POxczpTozF5cF1zAM
+QJTBjeeAX/PUPuRGjOOA7JWRh+CgozJzbETCnBKl02uR4GA9oWGdCW9fe7HFmGo6
+zIV2RfJEDj/PuVZaI1VoMUsXMK2g4rGFP24ufqc4ud3NPft0GoOHwuzsamMLXsh1
+B7VPP5NYpf4+dhjuFt+xUrga8Hdlo7ctFqPmyBFn4SDqL+0Lk+bIKqD8NLf6SyEz
+YAKGz7S98Mfs9Xq0/4QY9HOhKHox3gi2/b4KfQIDAQABAoIBAQCOacZqPXjgm0KM
+eD7odbmOnprdiXqQTnoyZkBxUtDWswa3T+ZsHyQPVSBQ62oWnGQoY6BytJ+ivoE8
+lXU62tAmANGoDdobbhkTn2fRcZey3mMqsRJi59lCh2Krr+/6lWhQpwnNqsK3Nwgx
+zNFZv4QVywP3e9MEoAVq1HEqBxfHRgBRFPb/Z+GS4Qz/QgXfGx4x492tcJJlL5x+
+VOBk8klsozzvWnJAyrDTqMlz9pamqpXV/CFO+Sxvcvzo9VcO50tjOqO/nGNg/SdN
+JV/xXQMHPkK6lgMLPG9Q6OhOMoJSKdEEh2zE1N7u0gFlVlw6oPEg5wt75Cu467Yi
+SKUNpmVZAoGBAPwvtxk2ubZ9rwru38sO51hAYCs3zDy8XP9esMdLQBAdIyXr0Rzh
+wUQSZElQ1CDNuGWYPUPxiszn33t7yA55F2JTVLhHIgdh5mGaX9Rq+f31VMjqZQc4
+GHO+Ew6OPpb3bzdeRVjdR7mCHU2/8P2fmu0gPrebMldGLVnnD9nxlv3vAoGBAMB4
+CNhxfeclaSgAOqmOtbTnSNKrZJ9zYPukApRHJTv6NaxCHhzWGfT2a+oUR5DEVwqp
+kRJt2BLnKAKWhsY9cD4LdzdbPIdqvhzvWHbJvQuXqaujYwW2G/OYqv+v5x66Oxae
+zq3bmQf3ZL4br2nQBHoNeK0QKJaV8waCX25mZ6pTAoGAMwwtkgsD2K7kSeBEqMPh
+jHmrfdQToY+3e3HdctoGo7xiKwDrGV+RUYgviK+14NYDp30DmcdBA21ETaimvFdC
+poKbuZmch8YHbmZjU4o8BG4utWTNAoMWYAdvsBiXDtQTTS/l9bEFHcX6zIw36f7u
+y2UljOD7dbMc5v/gs4s1tz0CgYBdBGX5/PeFE357t4iiU3cbw79dGTobGY7gbsZU
+VQH4t5bi9l1JQGvxCHrk+QIRQ+JxI7wZ9P49PHwIrCjce+rYAYCPP0fEhmD06POP
+DTaQ+K4mZmM/6mAd3UWfJqsDHava5csrGPsfb3+/pO+kqsTPG92bfjivdi6qka1/
+VHx7QwKBgQCXy+GHOOXHBfudpPKaSvwEk/E8nVsvuv0NrvQNUs2zKcwgMlYc1Deo
+wh/OBT77T8t/3UCUO2+PPgLhBTIpjF3qOzXQO48j+gWtq8vp+aYz09VVnY5d/z+b
+xamk30UedaJvwPY8M0XG/m9vCF8Vz5FqGwiZToJSzreZURKMGeEGiw==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Root.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Root.key
new file mode 100644
index 0000000..39d20f4c
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAtjBj2LARcV8DOOUkp4ic/vWmKlljexg51TQvJ0z+GCfrfnEl
+Ta9xl3/wGLAZp/2rUtkBqhP/P8nI1If6aVMot1JPkaxVyzh/YTK22SD0WG/DTE9k
+1xQ0jNOs9ZeKnfbQC2S0OlVxC5Kxjt8ud4r+NvYPvkkDPUL8TORQ9j6G0OQLFc0n
+Sa56vtcFKGj35zUb/CpQwWbzMRHz+UCAUTpgmodH/EaZ4xrJXHbZNEWwgtYG1+pd
+E87KTp0ugM2zXEcR3fGKl8eNN2oax5cTrb+chTLfIAqpJzvmJsadmNPR16AWTbGj
+Ox8Zw8WB3TUlPIaOi3Zp8uU1XjxsP35HV3/rDQIDAQABAoIBAGmzo4pJhKU5Eb6F
+u4Fz0lpeHTz+xafaQ1t+PklX8ygCqS+f55utyYKzWJKKQShlFWwouT17AqF4qgsc
+pV1MQRgzKjUDPnd8XPMAoHNTGlDg7vcsLP5YG7EE8pk+hc7mLogdsi8R+VdUka2p
+sOTsgFdU5YdqBvYZEhZudMaZOlCgshYnbpz1QTs/9Y0ev+YSoS0+NDzN0EQaBAKf
+m7pq+zSgyQ4tvg2wzIDrkydN0rw8kUa2kZtOumpdGC01TFKev56Ko9PfEKVCTyjD
+QAbYSDuUeN9ceMPu/1lci6dK7d1gx8/Gnfy5jpPTDIY294McARfvgUCK5O0sbHvz
+XDftiqECgYEA75t+bveSjrliAGhg3ELk08qGXem1crYqZFAgLq7NzKFFeELQrc/4
+3+eYZDC8gqL3mpZsAdiS7Regm/aR6zc675QcU7c7qGcxYr2ULg+j7aUKUGAtAuEf
+J04PHMz6X8a9SdnTNTZHbtj5I55L8hkFsENRv2AoBO9EQsdaEEaKqHkCgYEAwqdD
+8vsP4DH3VSVDdsOqH1OtwUhYfoV8jnpljK/48EThK+H//wZJj1VmRRxp4NgxAqFb
+ExPAKjQPp/tXP3hYPwrHXNHPJNi2PeZFYmpg0kVMz34tumpj2S0NWj7cgE2+7NjR
+LK6NOK4EdNv9ZKakCDvtC6+yFybHPndeRUb02jUCgYABUeDzaYe0I49Ho0uNSw0J
+oZ7123i1Zg17uflDaJEXpHfGfs+5dWDQku0C+EXBjnZAsr1rkS7WAYBP+564Jfi8
+Ixu41lSMy+y4t4SecFWd1H/nC3CUCHtscwCgTvy+FFEsm/eO3nqsQKO2r4OJlNu0
+KdrEbBosVMkSeRik6E6ROQKBgFVrwc3nj1f7lWawK6L6yrVkq2Oes/cR7U85N0c+
+EiiekZIIY6cuwyk7eN3rUitxtFBLLwR4LmRW5Gf7TJZ14YQI3uREznqE/7S6UMiX
+llWwQ7zqynZ8KcUsmCd6XpmPhLG7lE/faents9b0k4aP+nwCkEwIlkbCpb0r9RrB
+wMHhAoGAOEKKGPSOR6B14ar4JQpifQXUhjTgAMsNzJKq/Y311YK81XdauLS5z/ze
+Awd2zrs4ep8r056jSFp9UMFTTofBBxrx/V88yYDVdfxSpLtMkpON+5P7gyI5FsJa
+8PqJwJCW/EK2n3+IWJR55zU9kkCAbTVTa7JETCAjgenrrKLX/Tc=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Target.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Target.key
new file mode 100644
index 0000000..ea50af86
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-fail/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAu9M89Uzfc2HJ0L5WuH/mUlacO4SDI9jqMMvMAbodNnDTTFhi
+dC+WV3zlsCdv+nLAWwsM9uweO8cERbiJl776SSe2wgopuJjNpKRUKc5VxZH/idNR
+h4jQw+8M3kOw4LnZI5LwBEK2UAYrGnuXPmek7Xcj5YN2dmMJbb4FbvyqoMiRl5ct
+hQKVwvzd3PRLCMO+O0N2lszsVXoPAP4pS4fK31C6XGDlb4zwVntbID2H/YF/YVFs
+RGFVOlIoz0lNcj80sKMEGOZHUMfw4aVPjFnjc8q2pg00o0D7QZeMZpNkKSATG/Wr
+aXQRiBON3BXIIqIrFnTy8YsnwVqcxQ6VeLr+nwIDAQABAoIBAQCW4tONm/vGDUC7
+WK0B+n1kl/9aMNHI8nDwUkfI+2KMYYbdRgORosj773H1WTkz0QuGGBKKKpT/IJnm
+CKFALkOSkTzYFKH/kYFiSkDydLeix+6pIgHVB6vuOxPzWh216pbtZRU71vvuvYXS
++IY/s7NisNs9faak5FqmtohW6NOJ5TL/vfIr+cxqhPxW/vdBFE3HLDiTcgdLKOk0
+tYs7TWA/ZUV1RObhZWA6oZsvmUqZX0Yuyp5vNHAczHLwf7uA7Bbh3cjgb7OWE95S
+aw0ngSHS+zi6NO+J8sXuE7keaWnpPY1qTGE6ihzD/XbZF0WchKvlqAmj/IT0WOhd
+3bbbY6ABAoGBAPnXNuVS7q6BTSV+cCU2X9VvjVSjY0O9GETThwn+TUzGbRO7PG4R
+7hQXOkTpA5g2BDAb+pnq3MK1O5KnGAxJfR33FwYsGORZA7Zr6yG5Tqq+sy7GJOGW
+PYW1ooMAB4mXPIm2GdkOo0Zh5BCKjB9f3Lb8ZacFVDuxDlEy/xEl//4BAoGBAMB0
+oitIntAXFZDJ5xQfRyr8hWRurHmoIqxGcfLprke7HR8NE/3P2UewP9pD4spORLZn
+X2SkqI7bdm/k9d+2PSs8Jl9C2JH4PGhUxbv9r7j6qMPK/aIP14q0P9hWY/rYByL5
+q1/MxqThhDMaxp8iVlCJN2ffRRO8wGNsEJVHNDyfAoGASsqtiVsZTq4wjQ/bvJgZ
+ekiJs5Ox7J5X/IqiO1CgjWI9VxHPFlhRwDvv2p8yz0ckW86UZ61SZwtgCRfycAMz
+7FuCzfs3fGxVWy/VVOQnc5/g/hidA9c5FaT5QGQq3Xqjycn01PC32iMF5hnDtsS4
+yyKlv6ktvSzUz2QHzXdlugECgYAVCyvIS9KBsmR7Rnhr7NedTatQRgG587aG29UN
+2Jtj4IPYp1duQ1Hg0tbIiO+9az18LGVz3cVIiZqztXdlFMovdg5EEE0Z+OiyB8Lv
+QVf7g/z8G7AMDmtlETyB7UBVZ1Wwb1hby0pVMQuBgwYA1IJXoAlc5D7rX5IxzNkr
+WXPOxQKBgCvcgzMhBK7GPWRnVn/8eNrsUm/tmSQpuPbLbbLvpBOxNdUhVbkzl4Jo
+mFan2qX7GEaK71kGtT9mXONCU9nYueCPDI5xAE9NXP8+C6oNKQr2L/SxE37CTjzA
+B6ONmkgeSdBKDRW6AzXGwIz3KFi/qQ3vS5gDEWg9nIJtcxuko+pm
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Intermediate.key
new file mode 100644
index 0000000..f399b00
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAx6aYqb2OJ6vn9Z42pKT3t1kPAgpapwoE2dLfQ+QTYcdBM88b
+PuX2dDZr2yfFzwA9xt0q3Rsbyv3QS6OQkmYZNku7m9x0pvsj0o9udDUa3xN6QN+h
+EvMJonA5oOJcDraaTFP4LhL76tudam4OQSo9s9o+fpsqGirlcBsZshDREj3hnPOw
+BUB5w/tEQYABEC6Zcl/1OR5d9CwitcGb7CEpUPk2Ow6KqQvS5850FhB0Tvf1vBSu
+r3lKgvUq4irk8eEs55EE/aU4CfYhzmIuZQ0fMDwR/uV5hVEYleadFYL1ItF3tU1k
+goaEjFmQhrRkHqbMINibCblPfVdStgCfttKz7wIDAQABAoIBABTJ3AuQmUS4Oabx
+mm76XnDQ7SchPN83w9mKg4TmMr5zqO5kGkoqV8cyA3kGYypys/wI+3WaZQJ1+0Jk
+/aDA0M8+g4JvKhZZABnkpXOkM/AWbxxiLLt0YwRu+xEtgLhnexmHhMgHYgPKalGy
+s/lFFLeteeRk87VV0h4iNEK+TYbAi2MAeDhbdKkPuMNnNMbUU7QO/b3dm/0IVfqy
+yExsahEww7XJYTSVIeelz1GJklIuSrj9QcbrQILGZCC+OyhvSHF4YCbdwqOjnm26
+jn26K5MpfvQ1HCn9HZFK5LpFNnNFy4tkpRR+dzqMWorShnNe+yrFEHcOflZrA53U
+vPkyVIECgYEA/UFBg5/LN2x/JTQBQOgqf8b6uyP8ubkykD3focqvRS9pYw40bCK8
+/2XC32KUdjhphQyiz6tCsCtZH6h/Pgt/Kmgh+LZsYj85TYBqlOTn1BOsfafrNN/O
+T2zSd1+3t7tpsqApTu9kCOIbK0HmZdO3iaYOJG3KMBQANiOQCVdBfGcCgYEAydCY
+17t0K6naSicBmFB9jYAT09xIllNsGpuRbufyGQqljLO2vkNxslDWMuMrapb7zIxU
+9MSzLEXxnlCw6GAjjWx+5LDxDPYFFHqlzzX97eAMDHkJ3D1Se8o5b6U29HUNLcdn
+SvPwhiGOVf5jGpUo7+G472baNlYVAm4cv93xVzkCgYBfioLAuUPdAN1ml5vxdKSz
+18k3WHg7SJa+u9jmHKTKoPxNFkrIkMJkR2uhAnunrdiBDSdO2PkrpO7WdqaqLYQn
+52kJfyicV+WyS0PqMAEVjOaB8RtWsygN5qvvxPh2JAnYDXwH/1/pygMd6pqUx65y
+C2dCbvjb8m+x/PCV1Ykq+QKBgE2cNZsJEKTV/gd0Nq3PjmkDLxzTYurEjBczaltf
+QYAV0xJn7kf/AdNUOPt61zB3fb/s26MBnfHRuBhs6YuDpUh2x9nEnf6hAdUdUXR9
+S/jVp2yIg505y+WlIC9qNtcNyJKpU3TEmOPMNcOmP5Byejq98HPIdvRcaFn15IJ4
+pJ4pAoGAY22ERQ6O/hRwXcgHItLZmPqJz4Ryekbw49ThmPLf9xNMIXzdm2+ADtaj
+ELxcocUaRWmVNW68gsPflUCPV2Fl1xa/efxM8flKJ0rs1qRALue9x1Ufy/mVmZDy
+aoAtwUweViQtR180e1HWyAbRqWncGKrbvcFmYpcNBgUZOgxqpW8=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Root.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Root.key
new file mode 100644
index 0000000..a7701c3
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAszWDdHvnxFa7M8O1GYZ5SFTgAr4QitR0nFExGnxcvccpTOdl
+LvRBfbkCEDicTdw+R7x2ULwSFsrWl+k1HIj2kopmL3+K3Y2bK1XNXNMYti8+wqFZ
+j8sYrciqo67SmJKgUETwexNzR2l98xpJNymfSkAfGygAgvidgAL95jfYqWtaO+LO
+2KBAeicwTOsKQqYavCC/Pzqwq+44e8kHwmuHVMueHGA2pdwB0UQN4v8jHUfXF4ke
+OPoJMPQZoL5gSqn4Ykq/8coBMxfwePP6GVgRy+/1IC6RgIUR7KxVUZ9kaodoDZ/X
+kwp6ihx9ZxBzkfIMgcbok1rXsWUs6VQzXjm3nQIDAQABAoIBADT3ehTxkjzbjZTt
+IRecQTh5rYPh/S2rQZP6A1NasmZ8+N37/lH0a27nQY7dzITOtbGqKCYQkCAgb3CS
+wtneOVJyiWU8gyScd+JFB9+JnOIr8JbB7aCsXGzwxE6Am0nw/GT9Gz6lLwtKSKmT
+eVROfwAJF6iFGDGdnZ96QuTKWMUpsKuh2xb/bBtW0zV0XtWsoqmhJAy5Ncfxyt0X
+34NLyyBQEq+HW6kSPVB2qMPXWBNRFVJ0QK9g5cpOXN9co5hvP30lK40XpbJWRjdU
+bKW0MhnXVDkQ143PlUZTg1yk401w6RsXRLYVHCc4x13h1yY0BxOCMFxSwq+jvKKo
+zqLt55kCgYEA5NtTB0X6q3yswttc9eIypVK2MU+BdPw6ihNwM1MXHWZ9y2LhG9vN
+Uc4F4a2PtpqrDKIMOLoUkhnxyvGo5LSg6k48C4eYJw267l83KJ7KWCthjraYkH7N
+rYWhlck4xpBkI24y8MYv/Uq31WHK73nvyvMGuobCBCmP5eMhqArpP8sCgYEAyHbC
+EkKaHz/JDq3M7rxxjRDDh+lc8z7kn7MJLih/3QsnD0l5dXKRExlrkVVWhoLcibSC
+Rg8Xg11588knJ03+zNZhpCD8qMUfXClnVeDIrCVjQtr3xSDQA1fVMyWlLu+6++7Z
+ujDFa1kt2o0RAdVNwdde75vLTJz5P6Tux8EAqTcCgYBtUj+lN854YIP+SN9tLXJX
++tzBTWNfyKUGFCcCvWxLRQxOPZuevS6lJy80EL6X0eZnkHkaF/l/mRkhgrLVHVvI
+0Tppn4oVDb//4kftBX1PBNoDXEIgtBH4E9+ON6MBZzQOoLOAxItkCW8rZR2Vq7/a
+SKEsNPc1Gc19WTRYm220ZQKBgAjzNGr4SkVG5cUgAVxPUYqIyxIQWzQJBNAUgD5t
+VHgb/VxzXVbfDJcbtW/BraFHymzjgEV8ewJEdCNsQbFBjDS9BZL8Xgty8Zl9x71P
+0eXNrYbYm+NTObZMf5pO/fcAgQqqeVIUx1upmaB+V9oLGfOjl/t+qy76ey5aQMbu
+WQc1AoGBALTrAwE851rv1/b6+Aq/oHMGoXeGBnKUotUOpHtbU7mnKrMXghR97HFY
+QT6/xLlGDL+AoRmVbNcQ3P/9CESZI9YCrRyS7tMSpj5sM/GFYp/GlBH3mraQIAkr
+TokFfo1SFXLyPN3/WYOCO4X8z8F9EyIx/77wRl1kMjRRLiCeoF1f
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Target.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Target.key
new file mode 100644
index 0000000..21dbe3d2
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-restricts-eku-ok/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAsk1VNrndJWyj41wvlZesOt8tamMD9+myp03zfSF4r4DPNGpH
+ugUNkK1dWoadwlt/R4wKRLbe18EX5w9E6ogFcF2BlYFEJLNwOPyrUx5BdQ5yTTyJ
+Fj65v+WdXq9W71Cg4Nq9lMA5B1K0+z1qT3EOTVW7aUoxW0oWYPr9QDQwcOsS0DMK
+nSdoui+/UXxf+wT9xgglHESgqEsCfPyMq7TpjMm8qxM8HnUNCc/JVtsqElzg4Vhw
+ld+Znskhs7o8UFomopUSi5+O8XbqhZSs7xREtNlEKKPxYMB76B8Bvfd4v+/Nde7d
+Leh9Xpc8twawFmwtC/IHf9lD8HlY/lNBwon2nQIDAQABAoIBAGADir6kibSscvhs
+3ObmPQWaxp8CYNGwU9cJ//NDAfUoHOwxyxwdundNE/c6hFtz3+9MNv9XplpyjYeM
+TmUpCBzBDZXPfT1yLx1Q2oUwxrjdJan3zi5farEuWXbyXpMSTP+oauxeMpeB7xlX
+shbDX5s/bmM9Y6SwGarxnUxkji1PUsItxLGAmj5mq3TAiQijrS+INHJKU73UNMHh
+8pEE42h08lSsHAOZR+zkx/Afm4+McGagMrL1Ql0KmyeV7DfqqP0nydBFIMCe61SP
+ygH1Qbnp4kO28RGNFcDx+Yq5tboLbI1YAPt8hUS+lkbABVbj9PKGYa4sq9PeL/p3
+uTlFP7UCgYEA1cJyYvqZ9IubvdA9AgPBOLFJRrDadCBykH4mvHaCB4dgDM2PX+yV
+Rq5ToWhInx0DAhQE/VqESSQVGIFTjvbHD8BkciW9HA7z8W3GvbdUUWWn7qAuTRR6
+ONX92YYxu1tPqzCVpjfwy8RLySosg+U8EQMXsp9bAbsIsqtGkbucwjsCgYEA1Yku
+rxyigbfEFDltDdvlyAgqE1G5CfbeMYWACCS4wHvH1nz7tmsRzxcCdRBqX2H1In7k
+RB+YlhunmAxJw6R8u7tAki4FqqmVtdwTCBulX8i20C3MQXzs/gyNnW/fcdKsqha3
+RkymHgpa7IeP2uILf/gWJ1FkOV6XezlK+POIhQcCgYBZC/CkxOp/kezmDKptfWzv
+lgMFfMT0HVQ8VyEB34hZZI6hprw0ZJTm5dYW5h9ikS5gnkBZ3mw/H9Xd6HoLk0fn
+iukNGCWIW75Jc8aX35gzdFqZsIa5O2+S36opBJsRBn/Qu6OLo8Ae0n4Tpgr3QvZb
+y+MCWRoLRYPhEjKKoRIzYwKBgDvaog1Pl3WIzxtkJV9XHgd90l1r8NQMMKfs5cBi
+mq7Jg3BpxByT0oAb0QKDQW3PBWlP7Cf0O08IHWgPObXvK09r42OWJtx5gI9jSqph
+JW+90RB1ZeWNYNitKBzTOOyswt1CVMkNvxp4iJf4P6h46ARMw9jthYxXKVrO6mbx
+zHiNAoGASPhvVK+gdJsDnbM1Rx1oITr+1LnObnG3MG6h7ur5AGWXUVjlrGbsdJbo
+fSz3zNwpxC1KozF9dzker0mkoGGtQO0CaudwI3dCcY/hf6AodGyxLKZGXyPpRKWr
+uGzvZU+wVGVyCZB/ijD5HZlEXO6n3lwKucTkal1PS7aLIb0YStw=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Intermediate.key
new file mode 100644
index 0000000..f6ce1db
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA0UFAb8slBdkp0KPH/i/wU61GNhmqsR8/eqLg+wMrd2Vqeevz
+oxYTNIM7Qt6iu+K/2NJ1PUg4hrsqfRSjiPd8APQKa2uqm0QkYv7bo0JVFWcqMv+y
+TYCT0ITvG9x8rFYtVAgC9hhutYCod1IfuCwJbcz4HASRYm4e3R2JsvEjC01MbNpJ
+PWGDcg9mNhI/8/9TUnNToco4vcNIv3ovExnXwijhbzIAXmSsSwV6d2JXValZg9Xt
+oy4oNHF5L7nDnt+zKrFZzQQAHYsRVq7GZ/ZPHVgHZeCwL+9Xbd7BoHxuOKhFJiGW
+4PbvDijPAXBX3CAVCK3o45h0jFQywSgX4N6hiwIDAQABAoIBAQCfjcSHOXtyWSLE
+Ho3Y6E60TvOxPqLjSTNK3DT10HXtJRwp+Nqd6LAeI04lb8LfxkaIGfkhEBdhzAba
+tsj3H9WimHH1dHPyzeN8xF1Ov75GgpIvrr4S0E5k+Wekc9twQIlxgGZZpUmNBZvu
+12SuNo299kLcgjMkvVi1OteK5MjWzOiqEw8BJPDBXJlmdPo0YIOxTR2+H+xHpuwo
+aMf7siXmryJ1uh7gOo8N6U1X0GpQ5UzmirU1vfauV81YYjc7EreenqvDye2D50wA
+tWTLpv36txOwWrpUJiEk2a2SlkTlAFOb3vDbYwrvf7XHkX4YKEt4d4jjbB9GlTOD
+al4OsJZJAoGBAPs9xCqsnhecW4JlQYxpMZ4bPRCn2dxI14aDUoterDSg25r12E38
+Ph9fM9FGBB2pprLGj+nJxfHocLcmLCzBAfblky14wafm7F2Xp9IyprcB5Jri/FXI
+FGckOD9iFzF+3UzzGnnUdpNJyqRhOpsrCk636Ut6T9DWhq8/szM9vP+/AoGBANU3
+5jq9pBBksCletuiuU+IdRSyjkTxabdOqsDnjK2ol7C8ajpvgW54tlKuumGAeQ8vL
+a4bWEOYtWtjVZfsA//AeK+qG/6b1nEJK+Cat9T4Bg1LRQDb7IMihgVL2ZJWJRFWv
++refYKP3qnyzE5A/I0+8PNlld5KUmS6aylJEvhE1AoGBAKKqmxgGK1WeJqGGbao7
+caSsfh0KkEPP5btxyz/xTA3HGGh8RFA5wP8O5L3aV0/dR9D4PrVfromxtUjfrjpL
+vLneaixGwxuyp9bxGfc+VDKpRxoBXN8tbAhbqw9esyWYvi/UNpAqv5sda9aCHS/Z
+7hKJgMMdrg/I1eshkyTaFESBAoGBAL9H2MmV3BvA2LEkgV8ZFbPiom47h03nqmOb
+22DzRb2Cq/JOFuYMTuUG6zth9N02CYhIw/xBCwQUaE3ilAyshu85ghhyZ+O2sCpg
+62J36W1pGhEwHDW28WBMU6LD3NSyQpXEvF4DI0W2KEKavNBJdDpSGxzFBJKBsTK4
+Nw27EfCJAoGActUtucQW5DM9EG3IaHBMM8jl/TBX3Le8UrKbnDpzglkbBeqw8WIa
+pwol7FbBpgkjF2F5fqF+Sd5OWCyHVglzrDSPPc72v8oDnJmX+708Z6YftHRLW4Z4
+m4YhijjwjfCLBFgtNLAh4dRM+t5qPjTA5hyyh8hb9UvuSv5Fd23aKTI=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Root.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Root.key
new file mode 100644
index 0000000..5b7e3bd
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAw5Zmx+f9IRTs30oFGowi2o8+t47Kot7X4wgFzSgc2tSZuq3e
+kgdEGFXntUFrOGQYBqtsuK09uE7I+oz8WCwsqEIIKLSFKqpX4qh2Sm7+OC/RFMZS
+bwWkiVTCD/CTgwm3VVaUe1dlhwndYeoaAjwkpcwt03wK3C5non+RrbR2dgKsf4Vf
+YYYMYBWggn+FFvQQjUkn5DNYdVVrWqvH0b09qDtoG7TeaInEh/6HBNRS84/6LkR5
+wWJGt4hMu3Vh/ebFavuoO++n5hoeRC1hp05jXma494VgdIvqIIKEhHH1HcYMwu4R
+eAGuRFrje5cuAdAYkXcBI3/SIXP085qUrZMuoQIDAQABAoIBAQClr91176LRyYY4
+Sd409Q35lGuO2Bn1C05bd0pi115KStvH9s6baihXbT6Sn86SwMhRrhq1/5xPa/55
+scF7eECEcRu0T+iXkiJNUmSS/Z/CPU+jh7YBcwhFhlW3ZxevZCW411WFfy30zXiL
+H+PUjNqG0YbopyYUDAOi9uqT+lJ3+KGzDV2ZMEC5FvPAYqybS2HWUWMkDsQWypf/
+AmngsckZKf0LtL6d2x27YXlA0DBkPtE5BHbIO1IwY6qBaVIvoxK9HNe9M01yjv/a
+mmnvYuK/oksi+cwkYuCxqSx9gLuKyYRQl6dDXMzZIEpmyevOh1qZ4Sc/b/E5QYaf
+R1qeK6mhAoGBAP4jb1mz32vBwTmIWaVE/VF1/Zg/UsFDolUoOlq8gRv1AP+/qydF
+31yxhAQgTuoH9incoIfKhKUuwKSYdeozE5AK4zdpF0UK61BO7QJR7PcDB8KxSyp3
+DeVDOAh8dJAXp6cOBehm6XO3BNmNIMRlPX4GVFudiY7IspQxci21wLK9AoGBAMUF
+K7gW0A3ZgqEitu96y5vdCrjZ/3A0XvB9OWpWvfHcKR/M5o8IzGtLOfr7t7Yp60tt
+YeDFV4TyW1GuuYv8DCnM9OdNLGN0zgPo8EP9kmblHAOmYiD4zXtlMxr94sZBkdFU
+YxpJvx0BIA3T2H9bBl/Y9oD2sGbPVNeQlEZxhHu1AoGAPSQbSvJ6YvtXWFcUci15
+4FpJq5I4f6Sc7m3iNCg7y5UTK3RaYfVuemd+wltfgPBvabzZpjGz3eW0lSTU4YZu
+Q25LIe6XmZW57TU/0hoRr4+8EzwCQHIqFqkoVupSRMRcIlW+WB5CNgOnGAvbAUT2
+GVa+ftgU2xQv2nVW6eZbOOUCgYEAlxZ6CnhkINrW1F9czpXqoqKGYG+89f0TeXVu
+nF/c1icx2lM11Ca5LObJlfGHVskaygMd9lMf5LI+2YsWe4VUhpHIlcCW88ZVXqY5
+6soAhavZKetkgUiLu79Fy8M7LzKFcnQ2c6huSP3d6Py2oCPb5ZDqqMeFS7Jfq9gR
+/Vt8b6kCgYBSLtyqxhc7YsQZMmLOSHG2VX+Arcn2D/mvHaY1bWYklM230/7zBNhu
+Vqy9Iia4W884pSz/g/WpVRX3xWuCjg4Hx/VCEM+4EL8GNq3VDoJ+8c1F5vzBgELR
+zaMgUu0rh3a1YBcDf3vTURmyphKjFj41gb2+WidlvrsnyJowGCm7wg==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Target.key b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Target.key
new file mode 100644
index 0000000..01f91e8f
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/intermediate-sets-eku-any/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAri6LGI33diyUDD+gtupwH17ISMWqrVVrvVVoDY7O5ZknxSyy
+mimpj47DxpeJbTHXpI/YNjdPM8fWQgMRCMR/NYzuDxt6MXQEqgHTHotbAZ1gS5zR
+jx6r5dyPF3dJ4/bVgqUvCujcn5YeKqFB0WcsnvN/lAxuz19VUjcF0Dk3Gm4R7dv6
+qpKnT1ApB2mvHaeZ+uFW8AM4sK5r5xkL3cMHMY6EBKW067i8I/NAsBe0q54/BZaJ
+/IQjzNEGwuSLxmX1JOtyMbxBfTrJVQgM7qauH3gX+Kede7GC9c6Ca6iyxoq5vqXY
+OfRJ4kxTMoUmU01EztU7oGvn2QKhWu/hpYGn+wIDAQABAoIBAAxviEDFigBm6F8D
+f+7vR/gFZVlEu43KhnmrClXFd2IPEDbUnR/Cj7ePIs0f7pDcOSAnoPEl+8Kfpt1p
+qKKunMJvAGQVuyCivt6AaNlKa8HuwXxEgvWr4+vyVkj/nEfpTI8aSgSGYZIHProe
+bzuLGTVz/wzL4nFtxgKrqP+XxiZdzUKadOHgkRbqEIyLq5ONhyhuzr6ruXgoT4Kh
++mmnrKPjTrQPA/igUysw4X0gojeO34byUxLCCkkSX1J/MWVqFvwAS6E3aQ6fkwNP
+vIH0BvqRPsCN/H0Vtmtux7fTfgZlb/tVpk+HQaFT81LyKVoyOBSRUS1Xlaus6tAj
+LykCnDkCgYEA2E2iWRf2498AlB12t9BcltgPCDW9GGAim42+d1EQyidwfEC3i/uk
+bB/jk3DYc3YmUBv1gTwcspTZS9GoQJnsOXd8HFN6Qj+lhxJXjCw2DtOeIubjYYvt
+7mWS4vmKhIN4H5MaQbceJlEyy/yTPPbAEbKgzPqVRJkWW7FnL38DpccCgYEAziX1
+RMMPJ21LEeYNcLJlZkb9Bz8zIiBpQPNXyZYMHXCKxDrkCVVkCjqpYb5IbXX4OsEK
+WtDdyHqgVS8kVd05pJV0m6dE/cLwC/3t9H5ZJ39837AQckMiiKDMxFf0K2xM9bd3
+JRUNVdDO3VV8z3aCPfR2Ne32bMvmciP7FWNmXC0CgYEAtgO+FZKg4ueIqRqSB+OB
+xj1RiOsPkC91b8g6+lRw+GtvsF8VFOpQVdwPuMZAnghR/R9J29Ilo/C1WaO3HYVo
+zoLJIVztiEnelGbO3NlnM9rHOz9nH3KMaQt4Kx8pfJDUyF0Uvy/EYyH4yMZlb+uD
+fGEABvzmFq9rrQT/e2w6OYkCgYAMx7+n7qve1uDDkE6fAQBWUepX66wg3n+H/k4f
++kRwAs0nkzsV9QxJsg9UNvbIinrEMbmRncdSKYANJ+oJxLhRIs7i44DcdpxpMenx
+sW+XikjUmVa7rrvSWp23QnipxIIU7bXeP6re+h4JDMa7Ge7DJoe5mjIf1phH1UE4
+tzveVQKBgQC6g1mIw/zEzE9C4M51bM+t9qyjDLHdJA+xBkdNoFhRUMf0+ZWqW/Qx
+LxB9H0gaZQLTkb2Q1ctl3mnXfigXTw15H0Yho6wd7Pkrs1uH5EInM9vFPeRAWzky
+YbjjCmkZnrhftpWrHISNdrcBR/Womab0AiIdZimr7thVvkvzLc0oNg==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Intermediate.key
new file mode 100644
index 0000000..04e08541
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAlj0GQ8Hmft7o59DgPtWoHwj+mO+5oz0PFKU+hSdqoqqMO/Yh
+QxItyJyzz6juT4y3P39JSaPcFztLKCqTruHffiKyHZxfQ1mlTlXd21VGVYNtSHbv
+P3ckWNN7eCgFfGZ8eZCZYbbMLbbvNsoRO7VlyHORsBASF6PhHOrH3JyzZj3su6M+
+meAEmE2ctWKtFnEiAGjv5kL2BShNiBYFUYL22IORDBNMPG7WIipS2jdWqSQYuo8u
+Zdk6S+CjaZQ7Fl5KzKhuMm34dBUyyJ+vBhGB25plzwEFL2VLcU6SHAZRfylX6SSf
+ifmAYyWXkEJW+OEmYbJIsyCbmps0Ti8DBqfcEQIDAQABAoIBAEa6YemiLiYKQPFp
+ZziNk0FRFbLgIK/VvfTFOmKrRMthqPVNkSknNMCd7RTU/aQbpeVRwinV53ZUZrR5
+Ht+U3IdgRCQmir6FRIpVaEWqppr5bYhQ3Z8uMsFShmzxwmtnQDBmv5691O+skH9l
+r6lBY7JzneGaHzpb302ixgZ5CwgVZGa6TpCipyuKsj8GRob6IAyLsB+jKkydon8B
+zZL58iH+KFVmsxEWB/IwcWRVGMIjBASflMWykGcrfFOEA1RFtQVbBzH91RAsfUzF
+ZH88IEbnCEgbUvrbOCV0VsCZVrviJjBv2YQ4KrmJOwMUjNZPtyLElxJG8TNNqQ2Y
+idr+9gECgYEAxQLzjhPpSQjZwL1N9u4MUXCavMVatcdXkN2yq002jD9tx73SCj+U
+LbXGZzfN7uEYbdbU2BslHyACxu3RMgLLjku+p8OgS6bcbMR2AMliqE5hInJpPC2S
+XOa/ukz+DXQaZd0Bq8wZvGCsx9P1spjMvIKVxQ9mzIfaayWbo61gJCECgYEAwzji
+OgUpSlcwXrBUMMIbMQnYkODXLObqyjfdYHc4u+nCJ29nHW5cpRYE86bOKPwUnGFg
+/t40LQNTkHALCIsFvGuJ5Nv44zDtWrqEflj0ldYUp0cn2jbmnP0bpfmY5Ss0Gcdi
+RSFjNrUMlXOzX33Jxk8YcydY90FtWjQSK3PKufECgYA0eL/DtJvqntfYVNMiFG7l
+dfdQlcO+LJSu+c0vgMYpfm9PxsfdgOeHcSWhsiRAHmWyQ4i28ivl6VG0B/0ys7nh
+cV0bM6hK3etRcNX5CaokJ5QJZ62NoJmd8rtX9E+p56VsQfvC9P5ZxOnf4x1KbxYA
+k0sujBaWHQzYgtC8PF4h4QKBgAMPKiRknlAG68AgHUGiWRC8qcMjctvGRpmipHp1
+g9MWB6/chA4nA5amsK8sxwBHav9EW2PDEag+7BlQWCvrGczpFoEanVzaEG4ijB76
+v+J1N1+jstEtmRGOudcxAR9ePPPGdxjEPCzIS4kwBfiSHQ5ZgPWVAUTmppMKS9qY
+du3RAoGAJo1XbdpF3eiSlSMP7N2ocfsbyLdunJcU4puKGYGHsthPhT132ZFwvvjB
+N5fKCI/sYV552E7HsPnvJIl7qWQKCLIO9zqzLyYXSPxVqMHImvKyDTHur9AoDzkH
+JMDnfHD0EsxInQqXJIWxY2gqo44rJ1IwBtwTDrhanFmJBaWU9Mg=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Root.key b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Root.key
new file mode 100644
index 0000000..c6f1b8479
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAzeUJqYmQk8gzblvb6I4SsUrdojpgrecmmLxfYxKNOZc4U5bv
+ZdlDBvvnH42sMoeErlDMj0j6suJ+WDIseKDGgkE8TaEBQw/Ep51dJBY5tBN7CbHc
+P+WsO9dTtllSgIeyEmVsnP4kkojKWuzkBIGkiLI/OeFNa5HbF3bDDmekXcjkdhZE
+93au22N/N3DQ5fzfCA8u+Qhy9mVPrxWXo0wD+Y9fafiX2N3+5ywdo9hTRt9cxY7Y
+OEHO1+p/8TsM3RPd5y9EJKol4+uhjkOxW+O4raoeSfB3QGTvkK1ypKDTlWmWS20I
+NJfMXlylCMn6ZmDs+aqG2Lwht3i/8igBQT2knQIDAQABAoIBAHqErik6eNPXr/Md
+qlBKKolheV1Ny7Xv9KGyhjhYE72PZK8pA6/hhucjArdeAHfY+HUaE3+396Z27KPi
+x1/InBdqZk3zXuLfATCUjZYpu5CSfwaX1e4X51N8oI3DvTpKhQY5YFZTTMP9rhxJ
+Fq4vzw6gssrobL9z/aGKxCtQNRiwnm5mrj/x26j8t1bh3xHzvBb8jcK4j9R/F26k
+clZgTSXWjXTeAqgmwOfANtlDthNuLCXuC0DZkbm9wgDBJG1Wg5c9fBuSSRblP4cb
+XPu/xtd0wfykD3Qoygr1eGhoFS36EBCfm7eWbsUP0foXDLYq72O5dDvzkJruuuP+
+djxJ80ECgYEA95xAZf46CfVnSxTBs13MrUMUcb5ns6NNMMC5OLqHgdgtNLdufvLO
++kAf9GfJBpoO2X0ZQP/rUAKBH9Gfg8n5ihHTGWYzmANryBmJ1gC99tx5yb4Qwzrh
+2C6NSzL+f2tVEOHeyee53cDJdZP23dIAg5JRGdK9vkqaokJhvchG8jkCgYEA1N7y
+yiWms6muh/Pv8jHY8+bE2nVqu0YhRam8qiU8KwT2HulXxwNGN9oSdYDxfPf9JAGP
+6AW/NSVRPwxn1twttnWNMcmKkkfO712gX4RkuCVtIj2PTSRnGWgkTpT176mUZWmp
+rjP14HdGLXzTl5bVgtfYWywK4ieyKWEzzzYcNYUCgYEA21iHk9KKB5hlUJWqogJN
+9o6d5cUOiIv5LV8MtbxHnjaqlTCJqdvejsGPjSsDYd3HmdLANFyBT/dn+4/vBwg9
+DpqLrsximB8vs6sr92/g2HanTJgasVfQCXnzoNIjsSybxsDQY3vrow3NaWMSJZ6K
+5gMP5RhTDed45JR5kW2Bq8ECgYEAtmJbFRXTUYXlcrhvckyBPOAQ87e8fb4ljcFT
+U1hZx+YVVgDJY0sL45ilTiXvQgpbynjIKpyZ6dgSV3mykmXNiNII6opqftClnXLT
+kGMnxJrUeYzS9d5ls2AGE4oPeYsLCSTR6967INowt5KG3A+w2c1Do0IGBSTLwiZ1
+NGmBG90CgYEAhPMpBUyTUMxfyfgItIVIN/1IKnOTVMMdJ8jnk0GUqQIP9v5Xslzg
+/PJexDcm9aAUAhiN/Q8dXSHZ2dgx1HkIyf4B+jkeGLlHfYopYReOOBMjTDaEk25v
+Rgc6fTtlmKCXa83BH3m8fGOoA49h0vA7r0JZqSFtMk5LKBf1GeE5hOU=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Target.key b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Target.key
new file mode 100644
index 0000000..3f9b93e
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-lacks-eku/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAwJ6ld3AovI8YzqI5ZYEY2TEK7iMJgc2CNpilgi6x/FmoRkkG
+FmDrEkYshajAdXstRQ8Jutap/YgP1pd4Hl1RzBqUHi64gk7jDKpYG8bd/dKjkmwK
+rl7KVoeabL2JMplb2jPdeu2nxeEC3iVbF2r1FYoxccfAJHxVxI7PTAP9f/ThkMwa
+XrXxSy56L2kGKoZ2M4jvsuCEUpgD65h7FiVlaqZa+je1oXB0XpaHYnik1AVglzr5
+TIZJDVTbeY1oVliD7o8a2tc6BnhudfCpmToiowbuWKIr0epKp4rIvUP4sXBP+1G2
+InjPXMbcgDM99ZJtkckbfoc3KHa0/39tUpvH5QIDAQABAoIBABqPhceTerqNjOEp
+c3qws4l3fsdZo3z3pqX6pI/v+nEOt+qBNUqusJuDe64ul+NbSeHE8hWGdkhUk8KG
+fnTJb3cSjyweSykE0cA0WRPzdzcB7bZKGarzvTjzV/L4q8uDZCwRjM/fp4vcvDV/
+tzDiecsvlCPZZIBjeLwy6RjVUAZts3feWh5zhrhb+R+8xsVfY8ePD2JcF6NsQa/d
+zDg/SEeejxfS0oAgUZn3/5/xX74fhtyWkVaDDH3IXKXtOn7cMhzTEw1W7CcqzGeu
+CGitu660+B8ECRWBaxjlqp0RkYWlB2lcrofKh6EcdTM18zuMloeG005pOZyg3xa7
+6qAzmz0CgYEA91n/GiXL+9dHvkEU4dvrUk/5iuIiilXIPX9SHXukiLQSg7yVJJr/
+0TU7qtNdiC9ojxbDeGO7Q51ISI+NmQf21xJlVE2F1hxYMqVU5hFm5iC3s9V2IXfJ
+00xuInvZPSFhTumGxPOCU0D3cDyvDcQr0v9hIIHdt84Y0KOt8FHIsbMCgYEAx1rB
+C/vnzQ9uPdKu8mrQ7kP2/alLyVVz7sIuDss2mX5hHQ7z8ZGB1xQeyhdAD3wYjjD3
+YM+9evmrrhKKQ9hRujOw/RJYzFt3sbsRGIXPUR6MVj70LMFW+wP0Eb1KQIc7pcHg
+eXS7orrXUU4M1HPrhDfGX74vEDK447fiVU/3ZAcCgYEAvIPepPMBkCL3Ds1TOP//
+TXCeY3cNlBjkz9nln4rIT1fOdJCZqphnN+82Vm9Y7Z4UNlnHCE4aPuH0YTjnViZ3
+vlrK14Ft600W+yJ+ngnNPr7YwzqBGejN7ThnB+kUHD8Ahr/8csaUIRd2fhgB4qAM
+Nndkcv4HGTr+NqVIv8vVBXkCgYAD2dr00k7uBShKBXHfuPSERcfVqpmOMBpnOFUP
+cBmD6Y8SsqE/v2HUt8zIdp6ELg+DX6rHsfulDoGkgTMukFUz1Z/Lo7kXaYTsaAKy
+iJMyq/ZmDB1HaAy4GKF0XkW67WHXl+EwN3MQd6+FII7a48pe6XzpiJD8LR6pN3ol
+z7+lrQKBgQC6Sg3ccLIbxKMN8gP/Ox/kK88FNLsjeAuYk2DrSXa6SuE0wkozd7Y/
+yeXgFhkLhE1jrBM4NX1TPy+YRkNiY+cfhdoC+4hv9gLT2VQQJuDATgjMJ8DUL4jB
+CQYbGDIzWS43E//cMNJ103X4zQyLILny2eIwNZqUIOCR1e+QlL+IZg==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Intermediate.key
new file mode 100644
index 0000000..d742eda
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAmotEV/vQRCTWIDiqGyDMZ0AmwZgqbpXP2yw7psnmWAYBxdct
+Og31RZJs1xZ4WzodFvc8yZsalRf3zTB1RMS9Qu2ya+abqE4uzzYuPsPAId/tsgXK
+ZVkSySo9vzrfXhf266h45oHH1l6sm+mqxa9v0gQISp6bAmhAs8qNXs9ITf1E/o0V
+Gav9+34yNMmQFbKOa0qcY2iFu5G7G8uLwW8GMmdNDfOaq2yA83nxrLtIKuLvr4o+
+hopyPt+teq2QUORlaTcmShbOthGcNknahdqvX5HWp5Q6r5Zvb04Bo1EG53qrQaMX
+Ibadpqp1/wZ7+uh3WlivHUe1yIu/wqFqTAGC1wIDAQABAoIBABsyOOO21662QMxI
+zH6bpfzhiDB3Y7g0OvDZ9uFiFFwXKoazWC0oOap1mxu6w5FiR6478gGUfvgP0LbW
+OTzR1nCJveVJHslegNRMN5UqA4yyiHTUmgp9w1WNTnJxnM9FLlnIOwZtfkpWPM/v
+LfM97VKrDP58rNCeogxBr+EoXxQCIPjYSVoq7h9yTUYrBOdXwlzXInHMROYxHvan
+aNoReK/Ise1FI6hoOPIbfrhhlzcrJgKd+Dm9tHCz9tK5S0G0NX9TM7piz8r+9hHe
+BRRyGdQoGD9VhbgLgljtDEAFwuN/4sDVic6a0FjHgdc3Ihevoucx1N8bD2MeFkIn
+mHIc1vECgYEAyI0XrbzAM/wVp4mkNtGKcPkr5HgEOCNfZacgVuPcvd4w3Zo9WYr/
+LC/SUWuk31KHphYtBMsQZgLdM0UieOGgVSPByyY41ExWPVEfaeNUFIj0WveCHwvs
+MnIzBwP0cFqBnoCM7JWyo4HCFsdDkDqmb2L+5ImZplzutlGPQheW8qkCgYEAxUXQ
+L5JG24wbWJp90zWgAiyhvfVouiDi6KwhmFIIZi2xK4HZnbidEcQRFq3NaXjVhfsI
+PGyzVa0BqqZ63afKr1i7dBlPvBZubw77t1KRTDuAxfMzOZfI0TY1vrBGkgnyrHJp
+0Cgoq42SE+iyFq8xv1zuT6Z7gZ7x6RhUclmLuX8CgYBNS6wDp0sA/jiuYOtswWg1
+UKPtI6CkrmV3PWnGc35Bo6B72JWqrFrbAfdysCVUeW+UwNlLDqTcXGA7AXte0b9E
+8Uog7TNcB6v5aAnOevKOE5bydJCvPJ4ld0RZgNm2b/ujRnKKQMwgHsPamaRds20w
+YxxQowQYTZsno9muJH9mOQKBgHIsR6Ngu5XRbvpG38/f122quymf4S7oXatgBEmO
+IMJSa5nMm1BHStC/c0x25s3GW34hndCq8NgDO1Wy6KVkuU/mwQcepyEqslughlrB
+dMp0HcFzUhBhIp7DCzQD/bQEAemAhnEs7OztEMBpCrlKSDaC6II8znpkrYnExQsx
+fEatAoGAf+IMjbvgH3wm9iff87Go7pyPxpyBFcehuG1m3pyJFoTHuhc4ccf8lECN
+dmremvpSiLzH60r4RO0dLcNLXkVQCY6tkdfX0TBDPnzCcdZ7/HwOBLlUyVBrTLsY
+KTn9QYQsh2mNK6UBMxTlH0pKUe6mbvYeCnW+tUblTqUqA90m3uY=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Root.key b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Root.key
new file mode 100644
index 0000000..9277b3a8
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAqYaCEYNyonpahkJYroDK3P1i8BqttB5lNBgyGxA6YWBXWkF2
+Ncg0nOes3aggYOwMu8r83rtVshBJnlDvZ9BMK2YmJaxI+oYuhjBmO4vTzQnUTBnl
+fgH5jSmzWAmkT8FvpRkN830ifHSRq7I5tGp6osuZBdny9Eo5RyVULrc+3yeOsZYE
+MMKqcEbWliNHXzRA7UIRrFGj0aDFFxkzXORTJoumIbKH40M/jloEBGJaQY2/x8Ri
+7bt6AJtg3gAqXbJArwCinShasdlaTYLpnLJtVBaVJs8uWmmGnkAg2UJuP8ehHUDA
+GeoVd5POArLLyRWKnEkrZQeXvdsADbIevbMOVQIDAQABAoIBAA0n8SQmzViqoifV
+MkiomhW4XFtB1sUprrTyQ8Ex6zXvYhgRCHl4Bg0/NX0mNQ0QhJR2VlV6uFXPScdN
+hKbL1X1wufkme6tlimrDisuIOHGrF5yoTdUPlixMViy44tWFr4JihWCmD20VJtDq
+Tewgb0/++OspVN98eyF4ViYh9nEe4m/9/IUyE7pV5OuxCLY7dzGlOCrT9dbcJl/G
+u+Q8nRkQG8HK2DzHX0ofuT/mL9q5WFEUeZMZIGuknJTrf/wElRC8LMzIFxJJKNnE
+an2jTy3Yh4mUqyWaY+CZ/RbGovLcG8NpJVWlWYPy0Frd+pz0erK/hic9nqwknAee
+mX0NwgECgYEA0obDalQMt7kXDv0Q3p/IvnMRF6GN3UfOAk+iujnsvM9lCxGLJH5O
+6KXSvON+68dD2YgddGgB8U0ztw+QllPYTrFXyfXv7hT4O+xLdrtNY8J1JVagl/Dt
+MQlgxDujrGal5JlYvhTVjFoqDeSad4t/3VBfkKZkSHIqs4xZhGCgcMECgYEAziSP
+VCPacb0LYAfpA5zhuS9fskNVgou8jTorSmCmyLDi6ktREELY9EdOog0Vxzixb1Z2
+x3wGeA0//RUzoo8boJWbR4qvuU2kQfhBmnDBtkIEOT7OaZnUcgVTcrlrIM3Nv1ux
+nAC3j2REYx2s8SLAen9xj+9FsUIPhcYr83j+7pUCgYA+c22qsA4pvgVCE/4aHEof
+fODYIruDpdZNxzPdjGtWwysVMnoVNEbSKsat88plxPGyqPcb3fKdkypBJqPchDjJ
+d0A0j/lBpgTROdJVAVD+w+OeVOlEyVqDTmXfMFXoQXb6riauFF4YyXJqNqM/zSj8
+DOicb0+WUg+qvXqck1FkwQKBgGnoMpLh0Kq6mwt9ROOMSBOiGSI2ocnuDLLp/a+6
+tDVLW2lPxJf8IAZwVB/BZTzzDYXMAD5Ao/otpIBb0ilkKKd59UruH5WuJAOYjevQ
+nlUK2aynbdinJZRm1BaO2FEEKv5zF260l5ndw5zAdEd2uTi2HRv7q+yDqgHqbE4s
+DZ15AoGAY8TfuGR8SLlA9hNaTZRPYL9BGiUQWFjI9QuinJCHifLoe9kmlE7eCSr7
+9W131MU2TjGaIHSeFnDKX1M4NqGevmaRkmYFmIMEtItm75Ts4zBSmOkBBoucFZWy
+ESWLXAIS6IVlr51EwPdLFd+RnqY2qpyEUsuYXafAseEvz3SOAWI=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Target.key b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Target.key
new file mode 100644
index 0000000..069cc10
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-restricts-eku-fail/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA4uulEJi7acMU2dbGVxrgmp4wtdi34/1K7wgBsLiUCgrGJF4/
+UNHnIffODF+voatuNdRWz71F+241XQ80fjZhq/LdS9UjgF1Vd/z7DiyNhhPh64if
+afBFGS+Unvos4PyjI4yNz/B+ZZ8r/DQEdbhWqRk199I/K/DPGnbd2ggxm0yxgzEy
+L6scYVEludcRO/p/ixvJdlZ21Qf2lZnbPuDlWwRgk9NKC04GZkiik95+PC8GOLIY
+b7HYH6EQFgbQoWfP4n3gF8GylWw0snQz7OtxXqwlsoAfxqXqruJichgzUw5EV5Pg
+zToj8wvpmr6sDFd7UXYkJCg4jg3IhKwxSYd5DQIDAQABAoIBAQCmbI7OAkYJRjMH
+pRYoEiVCINy2sbAEfOM1NdkPg//G8anqSFkFbDyo0/aBargDyRf2ULoud7FYurZW
+fu1P15CArIkSscnsvgcODjMObSyKdhCOTtAjwTzcQOIuSmsZww/e5ZmoNMhuvXNj
+776Jm92q/Ttwevkrv9wUm7MP0kyXiSFpJvKMx33j6c1+M021bVx2TJjfKfcJ1jNI
+MF3NcMG8iL/MAwWgUFOYIMc0B6aTiNkOt3wOq+XRO/LCrnbkT5HFVGMsZU9K3860
+Z6KZeO4iMBCNlINQybXfqVgrvoA5Zqu6v+mkSWee2GcbxSNwfXyGqJg6eAHBRYrc
+RlSOqX+dAoGBAPH5EX/F3tla3c3LVqonK9Jd5jb5yhTO/1LmaPq3WX79ClEAv5FQ
+YVwWN9XQ4kG42vRUmYJNO1SQ4R8PM0MWn8owwKy+QyOhtqDXbTX9g31EwWOFZb9G
+NA/sbSNnmJZzQ5tsuplm1XuxRStDn66Z/WAxgP86UkWft53RKQIFwtlfAoGBAPAT
+MeiU1S3z8aUbbFQ5ETNHi+8xuldGyZaQZE+8CUFNEw33oT0SY+mMIFWZ9CHSpIoC
+DBkQ2YYdCy2miiEXxEGZA62vuMDV4I0K1o0D+QSRqGbYg2MnDQ3d+Ljx3e7oWe0s
+uV+UWh9oAHUl2QkfnyoTEVdeD6PqoMY/CNQbswkTAoGBAMK8RygEj7dvWIhRt/qS
+McNIjIj7+HVMrdEC28PCoUUA0jekmYeSH/ijbOYoCJ8J7TSrjSt/ilshifucGQ5J
+++kV2UpsiM35TGgfV6YW06aSGe1FI0CPeEDEboUKz5NtSiCgnX/tcavtW5RZBP7Y
+sUCkNoOxZRrhUj2xYgZdqpWTAoGAcKhxSTVefHv3L4WY5kUJX0j5z7tEOGSNgMwt
+ZoVUyoICqRFFZsVUgWoyWjkuqRiSAflH+BNCIH9MmZWHSFRA0o+dfEnzpvo2r7kg
+SXhNyOkZX3nG3iabJ6C8cP1/Kfd7C6NrMgEJ8ab6X/7sxC1EoZflEVygdklKPP2j
+hPWipGUCgYEAqyJqcOOZ9nSR9WeZNIkU8E0B/G1dFMwX37XJT/po8OZC8Yorbai5
+flpwwwkm2TCkk+ppt3uGOqvtOktDi+OxyrZHrzszkRa+uGRWfFUlyI1AePnAXE6f
+6euL6ffeV1Hy6urMWguXPB42BreM5/vkfVl+CKA9nBoRj7GHmi+Ov7I=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Intermediate.key
new file mode 100644
index 0000000..2c027f4
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA7Tu78osggd5CQcYkYxxL5WOwkwf9ImRQfe+P7WWquvTZrQxo
+3VCw6gpeGJ7fSIjsH/prSj7b6iRusaO7CxLeHUnTMngk+ehPqoWQIaIsj1iVjHCA
+jc2ZaANnD0jrlhdjkyuPcncjX5dPhr0X0nBbXBj4AdYR2MDcMrL0v93aZfuGI8Ck
+vf/CpLaHnhCY1PQJyyZQHVaDcgnGwbfMUpxhCQS7qipjZqWxAmCFvDCRYrtvsCQz
+6LWaEx86c5XV+7ypSN0UoqRi4ZcZV7Ea2sF5k/10y+H/DEnCeFeO79zfYJaO5qKX
+YLlTaxeOrvk9vjHdRhi9r7amAvpIL9jG8B+8QwIDAQABAoIBAQDIGd2DTMTjpgR/
+FFF249RErZyvuEpU6wZV426kUF/9CDBfXZtKKhi/oHUUEVXHCe8ZXhGHc5PtYEOa
+RL+tLIH7dFzXMi4GOWSRMc2MAQ3S72ZdKjvU4DnoWQ5h/yDv3dSYu2Joq4NDyw5C
+WVcxoqH9AfaXHei5ypsxjG8TM19XKnyNq9MlO0o9LF696jabjnvcm8Ymq2EfrZWp
+46XwPC/XSJeR4v3Bx4JQFDkuWeG6KWpLqmB1oX05WOoCvX7JonjX6QMim89L6zN/
+LKyZA8W9vlP9CBe5TwyaZC+JZOsJNZXajbPUMS0Pt3wec6hGyDNJK3DI8PV5p8Sb
+DSHJlvjxAoGBAPj5J/8LdGqSsSG1WwiyvT0prcn76B3iH3h6G1yb+k+HYddYA/+j
+5q2yE2AC4UtqVPn6n3sBka1S0ONuTOZXiEK/xyq/BdOW49AjjIOKCaXX89SaWG/q
+0RHZrkMnhsxf1A09yh1sOIfznO1kmGkrFiyXn7w7Q8ZXcOwAeYcYRQZpAoGBAPPt
+waIHhaoHVG1qJbq2bjU8jqSZ0oCmIIw3xqwhc8716lhXKH08uhNokym9r4sVfen5
+1Sdhmqk7L40P37A2wy7WBs6Sguh8tXJc3O00SnwCvoQU0w4YBnAHRx0Hhlw4JiEh
+FW202Wiot+6bNH5xvA3Qn5LELGo5RNG9lDvod4/LAoGBAMry3uWJwtX0yar+mDxY
+5uVqih2x2B4z6w9cCd3Nz5bwdpMBThEe27UPCbgj4N6GyMoUv9eXCdbNQTWC/fBt
+vccbaRCxMeCuiPlrYOkApqinhjzxq9FfChmQ5fobyEfkfYhlq9GcG+DGdk8UxyBD
+XQnwducLME4HjSbzpBy5bdqxAoGALKzX8PgVt/1drihpvpeY+bEcovL1RdCnV2cD
+wRTjY/1QLVvRM5bCsblOcq+mDgAiro6uRmcu713CqMBGhLyS5OoYFw9oYHIuvUJa
+yCrylWHfSMuTmBo4W55JnPx61DsIaLrpdM1RoER+Y3oTlDD6c0FJaJT7WX0hqJRj
+KNG7zB8CgYEA3sjJXkYscwRAzxpaG31BiuBV28bjgEO72bi/LIby70nqc3CBEDs2
+O9CVCDNlA1aXJOKLHbEY0jPArim6d/rOUD4ca3YmyCdNmq/dntRq1+rv8b2Redq5
+0xQMbZ1rY39FzOwBQzSKnQ1prXS+2vQ6qEm4/vRbUvCvVxWO97qlFk8=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Root.key b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Root.key
new file mode 100644
index 0000000..c4484505
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA6Bcxtg6EEKSbv56unim3b4Gu199FidEpUQ4eOXqWa3/AeN+I
+z9uzq41JD/twVYVPk58SoaZVXKmujXlNpjoyA5y/rZXEi0kfArUjoJ/a00XGjPzs
+l0ZX3XdWxqJGeNqiWbsi6t5jlFAZkRwQzWfgVxC94N5pZ4BtMahDvEksitZKIw+m
+ePR0x083UjqvnAOys2wmq2JhEm0iFWba7Na4H5sUuQScm161y4tilWdqoVdEAnei
+gT7HIFKiFi66wimhVO0zZ/IqJqO22giNY2zKT8aEiLlgCM9Qjlo+ddfs12PB/hg/
+TvsI3jlF0oE0jolaSM5Jv8qEyyaswvcfaz8NSQIDAQABAoIBAAe7/iQbB7umQKp6
+F0eeDEbjA3ieMCsPlVjmJ4uy0iBy+W8Nw/lpUOt+odiugGfZMXWx72UDrgSQgwij
+6jqH5fLI3npia1JY9XeZob5QlXJE+QDpzNidt3/h4jpsXfzZDGABJIC/OIJQyQlU
+7hpqQ8ei/zDnOIwCc6EcAmXb6mgV+9MA9xXX8PIbp5o9t0w1VO3w/WNl6aNxsKOa
+0eJWqGD59zfxSL+9nxNCaOyvEe6yvjEFOMSUqs4vn5CSNvHFP/j84lUuUmdufNoW
+LwJdkbrkoE/sdm89KvcPpj1vjx0pnSfAL00mClt5BM3DoW7+WAp6Ovm+0hKz2W57
+AUQraCECgYEA93FCVO+BEK5PIlVcIyZsnSIUGPNn5TfyY4Fu0ehSroenT4+o9x5F
+TyJ85lylxVgNDWvhBeGbETnEEtNfBIC29rTS7I0cyHYxmU6sPGpEgB+tB7wXGYpf
+O4dh8lBaH3hUo4i5o+xRKaueyJ81MtMvUXcm9O9dsmjRFOE9MJNOaosCgYEA8B4E
+Zk586QPUvvaemky5pZgm5sHoE5tC8JhAiOHBm+wSsT7d6OKQoaJRguRciHpxBlKr
++E9Los/ERhhnrkllVX+mjAE0JA/9Vb2kZJno+11Nn+ZUouUqbo2ySLy5SJkc6ckj
+pCM1psGuJiJd6uszs2OsUYjyeC8Ato2/lsszpfsCgYBugyES+hz0rEUfmmwaLtEX
+pN7A/gUK9N/G3Un3agFzfZaWDB10sQpXe5m48OwApYC+282WaNpu6RPPLKQlSK9u
+o/gIOkX+qfsg82gtW3DYoE0RRUoz1/8MgTyXkpeNsppqp2fx9FuTdtjl1WVXG8bo
+ZYT6o+V/Bx1KbAZ+KWw+XQKBgBMMXo77JP+bNTJdACH/ei6/zj46Hb4IcBwECAKT
+3jcPWEBFW3dRGeYoqUy37vtIs1SsFEZji/2De++PmhICco6AzOaIZemCdzdpDvSI
+HSprsW/A5u/xPBd/GCibCBvRQbDuKuynemcbMESIL2kmdXiCrLXfJlUQbX8N6af9
+wMAnAoGBAM16yiiEMFXwOtXTKOIYXdWDduOUAFtzUJxhXBLYbQRCxp64JKIo7n1c
+5KTpBiSZ4P4yIzy28cpqmLjQ+J5RUlMCq09qqBND7+eqeWQ/o0oBfVE+CNYsun9j
+AsDPqp1k4rAtSx16d54sa19cA2cSeqE9v6zLgnOVCVHjwgY3uRw7
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Target.key b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Target.key
new file mode 100644
index 0000000..ea8d581
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/target-sets-eku-any/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAw4ITZA41MwysRL5tkvXkl9iavWTxtWdiAXsMmFdKY2SwnWp7
+hKKR/nMLTIHOifmNjYpBGMjYZCc2MuY2JkQWEy6hrTgGCxs5YmqUrKBZvlLLR9dL
+AAmRjhRpqWLfSdi2eXPeYNS4domkU4odS4CIMegFRoEbe11S0Gs7Uw0lPJWbLZmD
+PAOMtXP7Q2yCs0hXODz/t3nYE3QG0Bd4qTgJdsr5t1qlim6Ffyc0eYLvogGTrvoL
+GEfUFP9neCtTkvasJ0LHf479Bko2uXqYXg2U7xr6CK2NZCjHwQN2Y7kzWp8WvtPg
+XOlDe5uDs5Ax51krHNKMcxWiOpQ1A4CX+F2jEwIDAQABAoIBAHZQBUQLhQ47oRSI
+2O6Sd5+cqSfsKonI4npa6KhYSuATUv4wLkd5yjfdjvzhoQfGSW9aN0K0aJ9TS21J
++4L0uRcqpRSaTq3x76wrK4W8FBMlL3duRijjoX4FxFuJRe7+go7FG9IlaOvzJ8qW
+TlwekE0DSA3+m47wTulpuyDrV5ltMMzWDY+M8IPv/WK/mN4G3ONKPWiWA+Qlv2Rm
+pmCMmSuI2ZgA/UW0VNvjAwrs5wuSCMkf+gaY1UbKJ4Au06DDCtCoSYC5nZqw/Zkk
+LUiTUrVAB4TpwfSc8VoDkV+SJ54Gw776bd99Poyi0qoxjlPd+frHgerIVMN3rFfn
+RHYRxeECgYEA6eVjjqeOD0bQ3nRKUWKZneqpPePtxxEAAMhRBPflPB3mR/Qq4SCb
+ZeiWFxJ1mWEHTvUsaM5jlF/jZvF+H0PfQiHTB3hyEdbOVIDSIiyy5qD+8ubaslph
+FB2Syq7lIeGTiN0bB7Ogz1+tfecbzz0vV/Ce43r6PHQYv0qwID7CQc0CgYEA1fv5
+KTCMG35Hzm3qvCLNlcmRYJJvJUOPMEhwlZKurrl4jiBGYHmDOK7qWpUJc4ewrkFg
+WHrg5KUHDDUL1XoJrlDPMNOVP4kjX2f5Qs6kej/W53isDmdUxD9BMVPhHME4kmop
+9LbyfMW1sGg416S9RJe68xUCo75Lb9Z7wGyMGF8CgYEAw5y+6J2lJ42YPZOQXARU
+aUfKByLKx8Ol9wGRENCp/N8cqmzAN8vnaxFcBSvBAmetjxFo9LY3fe2752psioVf
+AJX9QbAv5k95/B5In6A2dr+KuWbs3GDN897P14bxxqY7lykj5AsMoKJqHHPeRDHt
+mGR63dEJ2ulVkRZLuowCNrkCgYApOZwtFU9I1LFc0cxRZpsY6nZ5lnyXP0bM1Ifs
+KRBCVTUmnI0ydPaU6w33WZMykMe3Kp03LqU5J5oN/gJDpHlM/gCMtZahYPhRnyRk
+fI8vhjEO8y6ir8Gi9VTH/hL2iTsu6gkfPkfFRgnU7J9W3EQifODlh/y0MysxZq78
+yWzMHQKBgQCUZAspO30NRKD0+wXDPHpD4S9YMjzdIhwExbp0zAtk/+6vKB+eusn8
+EWmnQoadShchg5jaLAy5cH13ssLav1yegm41QidLCZrWXeYUi77Lys5y6/JtAAy4
+gtXNgvRGYV5SHR/2yd5DyZDfN4rQfX+CMO7TxFBY5+Mcu6OfBuYBmA==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Intermediate.key b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Intermediate.key
new file mode 100644
index 0000000..17dcb98
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Intermediate.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAqOdf+tCd8eHkh39ifhyJAmZkntWggfNlaNeNAjeZ2uiFAFG0
+aelXKQlRwnjI7ruHYkqoRsPUBuXwwjNoE/dVxURCFB7XZaShtmc44MJyZe6t9ZQ0
+k0/p2KWTmAU05fYPO3GEOXGbthBHN++H0pgppPEY5/Q7Uq80sTk0mkm0eu0hLGCy
+AejLtq34AJWFqYeRkAVUCy6dTHnEyG1yqyNb0CuQPFtT7dpWOTg3RUMXPYHVSZcj
+iIOfv4aNUq89hkXxHujdj0/+2rU1y+ACuo5rYUryxl3XApVxI557mZbPrN8gKi3+
+DEJyxrjDgYE+oI1iQRcU9SRn8WyvxgyUCftWBwIDAQABAoIBAF8LA5lRdu87W6tb
+ZQLt77LoB0rRjyZQ3gmm8XQD8ZGbMexCDbFjWmZ/FgDGktqzr7UBqbtYSqEvFtFQ
+uAo0LZF5nW1RBYfuogjQANOI13LAYidEpGkYmNAOLAVpOKEGWv/qGqtwYFyMwGab
+ZOR+N0DXYlpztkYKzS3EsPL48pQQnXCEVYD/PetjzWkStjKl5AeTBX1LJf7ehtLp
+d3P+HYgBHcuJm7I3mQbTnURl5WYpvl/+hDWIK+3w2BfaU/EF42gVZJCOTZDQtiaa
+5t5CcNICs7pPjXYbIl2wvzH/dQNCMH2lgpJfXeaMW/PrK22Ddr3Yxdmrx7MtfEQN
+f0l6IqECgYEA1ibuFrLCrNUCmk6Ry1t1oGZVa0gTkglvtfm1K0Y+pGeeKS9QaFq9
+6QT/F3IjHn5zY7CXbl3s5ezWUrislBLZ9rv6UnWVYAbfIMqhN39NAHUDkqEYiYRm
+zUEkuB/kpf+YYXEKt73XfAcpQI/OY511zsLQ44q+a5/MAwhmkJC2zi8CgYEAyejg
+dCXsW8G9L9+UafqSyorWX5NwUnT5m5545cvJs8UjP/M8tlF882LeHdbZ/FsLeAqd
+vlRHVEcHGTToL/Iqn/n+b4OdAX2W3P09T94S0Rbghw3o2nEV9zjoRWETZYm9ZWk5
+Q8fC1NTJbjM4tkVzGF0/mbTNoZUw40PV1VTpF6kCgYBVeNI8EvrqGlZgmg1MmeNX
+sqBTIkBVYBjLC7AIZo/n9EdG7tBr5hO4Jjtd8zsbF6McXcYC0dAUcLOV7olKzD9c
+X+hn8vA8lGGH5/fye4eTMCDN7Cgr8sFJGL/8ERakPmBmO8ToUwfnQ8BuOTdJwPXd
+IRpYRu77a5r0duzgtDGSFwKBgGoQflAlV8s/s91BKtqdOZ1SX5inf/qg3jqEfefh
+LJzV81V1ti/kEKpeBmZQZgRt41F06jaAWVQJV85C/7GoGgIdti3oSoLs8WI3WYzq
+EKJrzRjFEswlWa+b4lAH1cOiHq72HpHfjxZ0jTfpimIdi3+CBJX+54J5N8w650qA
+p025AoGAGtXM2FylEgDSTUFy0OSECV82Gyvywh7ShjVjch7hL+3/rrqnCUonx33W
+vzMiuSHo8wtVcPkezdo4nfRvhgDQVKTRRFRfukC2+uY9RL3v4q0yvLTxQuSsxdfZ
+tFJzjY0HypYIoEurGE76rrLSNQxmeuDash0HthJGjbb6lWz3rVA=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Root.key b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Root.key
new file mode 100644
index 0000000..aedf1e9
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Root.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAr53X0aWRbl0X1ImFlbjP4+P7lN3MwJlZJKzATcxLN4g4PKFg
+BpaNG2vnK7hxnlRLzcRNk7Y7P3qixjvqnzaN5bAPnidYfPj7b+iuDLtpAmAh0b3c
+4TMjjcVf3P8zcZWYdwdpwHEqv2LrtuXMLjqYHHukp8u65asiMvvVAxoDt9Gf2VZp
+rrFR540Gyir5JUOvkqH3QGCFWjNnKmKtbkqaAhvE44k40wbro4zOqMhJWk4Isn4A
+FpJgS/93LVPnLPMsUbMWh2coQxDTbNbClpejyI4LrvFWE7sbyn8tWcw3/Eed98kK
+ZhmHPRNmUAtSDRMzbAv8+4jPNHufb25+NqzsOQIDAQABAoIBAA5E5/1L06ZBACev
+yN/idVnR82YkI3feveSB+NoZaF8pvVAH2LKQVwTMXqspwGmZ9dLRSXBgFS5SO5mZ
+0rV/DEaGaK50CfeVj3CHWmUEcULVJspQ//p4WkIxsFlXjwmCmzHIs2oEh/GnCvoA
+b/etoSBnV3ie0B/LKOs1lRg8a+Lqjd1PdtdgJPun8cExfa9rmLPUAAuTwVrzKNdT
+S3IcxKBYBOtMJjGO41GG5bw0x7JrzBACxqXl7B3dC4XohtdL5bWhLYIxEUTF9e4L
+QGFYX7NRzWzbEze8V15KY33sbj4HRbRJwjsQ8YG+BSy6NgXdSrRJAm9oNTOAsf7f
+/ipDoGUCgYEA5gLFzzsLnKxgzfiXlpV40rKiV/QmoOz1E2cFN+DMW7gWBNKVcMll
+b6xKjMBG3ALuGobcf/EFkeZD8IIlB1GkI+W6qFSFLktT2TGSOHgYeBJrPuZ4l58r
+xF7VBpP1CBAudUbJ7oACvag/XlrNUbx76R8fsK7PwA3ofXL1RXGsdNcCgYEAw3Wt
+fTrSvLE/rDE7v/nVJRyKFa3UBjza6vvxZbdnhC9bmYr2sLw51xipICeOK/MQ978i
+EeDGcpWPOvPeyxjC5MXUB+zbj3DgYAGd8Fiho27pmlIWL7q5BII5O8RCAIvTwFdp
+CqXyzLiqvRGvIkzfwprRiJ0rct9VF4bJQ9CAdW8CgYEAn2PWfge1wUl2/+S/71r/
+Ukr10ytexW/PWTWv2QwPsZN8trTTWEhH4b4sHyNzNy1UoM9J5+M27+b4t6cIT+0U
+aMfetJ11eSI8JauDX1xh4HsrFHiTosZrhvYMezV2vLKx7xUyA/NzcsgvuYwE8hpC
+Z8boqABL+RPqQ1yxeQEP+BsCgYEAmmaJJ5WiBwCVZbZ2lo4KO4ix47Iu/MZxwJJI
+/KrGkPrAByho+u+VWT7Xyti0TC90ReCsTycaXEWcoVsnsZPb0NAdUC1gu4zVEcH1
+O3koJmxlCEyzzfxYTyF3iKjd4oSSPyxNg+XXSLTP2w7vI89KKvYVcy+EtPeKxkzp
+DDLWZu0CgYEAjSeoIVpnaj62BJgvdcTfR1nfa0StW1XKjETfzrSrH47G2NVdfHRJ
+nQOJpgdg5hEr5iRyTATZcbV+EPzcvsiUCGxo+0pRlsjo3K5RkHW8FSA1aBufj6/Q
+ISLrrhCbDRV9iPFvqPnybXx21MK5qnOBW4fbc9aqvVTCc0dRC3qsX4U=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Target.key b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Target.key
new file mode 100644
index 0000000..bdf8209
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/keys/unconstrained-root-bad-eku/Target.key
@@ -0,0 +1,28 @@
+openssl genrsa 2048
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAwGSnAbKDbEe8LTAB+UOM/MxrfKTHHHj6qIy+Hppy0DQaVoBn
+Z3ZIip/FOmieU8I1zml+T9XE+wuRPK8AJvS/d8rN7If5bgWbDJMb8m7IEDJOe1Ec
+IndMuKO91tyVKZtLtdnOrpHYBcXFv0qct5Tb1aXmsUThAkoa3CHl5qa6VC4sP0D1
+/Vx53VVtnuKr2zxntITbuob9oLXYi9C4vIt36TIxUWjuGBcJ4vEneco8cqjzliUx
+JDoFU9SJCkh6nC1taoSX3zTJIn/VBfIskenEf6vQrnYiZK6+4n+XCOyGipK/V/Ai
+95H/hhdikuOAixmEFGAZAJHW/lGWd1siDTJQBwIDAQABAoIBAQCTaows2MOqrCyb
+0zUv8XqOGXQixX9raadmjrD+cgmXTtT/gtO4iwF6doGBB7iJJT5lMp/+PCX61hor
+DUxojRBIkbTHkEim7NSpBe9cC1ZwUpugEb4lCpRWxeAoSkxg3QVeCc48JLQJeEjV
+Ei6S3BS117C0E7r1LiL2mWK3IPmUlZhPy3HcNHXOliMblb6lK6zSNH7y9U4KJQv+
+Fok2uVNiJ2aU/8MYHlpfVtLoRXs8FdTMheWnfdDTu7q3w+UvlEhlLOCieiwr4DAv
+daD2Ga4+TaamKdLNzlWlIxip961jw4d+HvqZl1h3VcpoD9i5wK9jlEu9bNrPdKrR
+64UyetYBAoGBAOM7neYQ9l1vdDDOr6KZDPa63U3JrfSZKp6DCldt8+BhhmL2ICd9
+EjgkspNLeFAQr1iSXY7zaE18MfDIgfFhc/K8/mIGmDFsnaZEZB8+TuU4a+MzW+yr
+LEzAby4z5JA2be5eTZPzbf/fAkUXRbPe/9ZYORWJG8/MgizqhrQXw0ahAoGBANi/
+6quOZpcXZeYOrQJZ6OAKVBBpOE0Zt7s4rtn1oTTGTQVPjAcgSv7cn058a0XlK85P
+DZETJtUTkGm+3dbSwlRVZ4Lj2ozUxoKw4q0jq6co7XACqlW96I0IXdQRWT9lTyIg
+Yy1LlX1Qd2+h5qKoIxgJYM7D4wrC9mPY+00Obx2nAoGBANytIxUxV/E5sh9MOmmw
+NVTP+Of4ewXWUfuKy1pJH9TDIZ2t5WA0KKN5kCtX2cn3yjI8Qrv3S04k1OM/9mIT
+AGW2gV11hgxJrXixZoKpIjmd57jIQfe/7M/E+rRmFQywr2YVE0Yh3KvnSe8LQNgE
+M3VYTGfLtcTCmZFBWfxAL5bBAoGBAL7u2IcFawPf3ah59xeiIgzxrDnEpo7sf7gR
+550izj4SDRkHiL9iSA4YRNE7sregeCVF6BqK92Mt18H/G6Y4hG0LyqI3m3cBnFjV
+/ugsCvK2j+pivq/HGcrauuSr36WD1eCnDRaChY4dSwjwYp2YZUmwQTICxsbdFXTB
+WCX5+BHzAoGBAI1K64rT7krwdaGgXXOopeDUIuPvzNLW5OkP8BkerhaDTEfoMR+H
+C8Uo8mzSmKcxm1x9Usd9v+Kx/Li6FDlh6EQVZId60Q9T4dpV+EXN9eFI23H+YYP8
+pTcM0MIlMhBU94Gjkees/Zm8ibyLdKdY9cTTtchBmpx1FeRUHpyhsSAf
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/target-lacks-eku.pem b/net/data/verify_certificate_chain_unittest/target-lacks-eku.pem
new file mode 100644
index 0000000..46c3751
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/target-lacks-eku.pem
@@ -0,0 +1,285 @@
+[Created by: generate-target-lacks-eku.py]
+
+Certificate chain with 1 intermediate and a trusted root. The target has no
+Extended Key Usage extension (meaning it is unrestricted). Verification is
+expected to succeed.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c0:9e:a5:77:70:28:bc:8f:18:ce:a2:39:65:81:
+                    18:d9:31:0a:ee:23:09:81:cd:82:36:98:a5:82:2e:
+                    b1:fc:59:a8:46:49:06:16:60:eb:12:46:2c:85:a8:
+                    c0:75:7b:2d:45:0f:09:ba:d6:a9:fd:88:0f:d6:97:
+                    78:1e:5d:51:cc:1a:94:1e:2e:b8:82:4e:e3:0c:aa:
+                    58:1b:c6:dd:fd:d2:a3:92:6c:0a:ae:5e:ca:56:87:
+                    9a:6c:bd:89:32:99:5b:da:33:dd:7a:ed:a7:c5:e1:
+                    02:de:25:5b:17:6a:f5:15:8a:31:71:c7:c0:24:7c:
+                    55:c4:8e:cf:4c:03:fd:7f:f4:e1:90:cc:1a:5e:b5:
+                    f1:4b:2e:7a:2f:69:06:2a:86:76:33:88:ef:b2:e0:
+                    84:52:98:03:eb:98:7b:16:25:65:6a:a6:5a:fa:37:
+                    b5:a1:70:74:5e:96:87:62:78:a4:d4:05:60:97:3a:
+                    f9:4c:86:49:0d:54:db:79:8d:68:56:58:83:ee:8f:
+                    1a:da:d7:3a:06:78:6e:75:f0:a9:99:3a:22:a3:06:
+                    ee:58:a2:2b:d1:ea:4a:a7:8a:c8:bd:43:f8:b1:70:
+                    4f:fb:51:b6:22:78:cf:5c:c6:dc:80:33:3d:f5:92:
+                    6d:91:c9:1b:7e:87:37:28:76:b4:ff:7f:6d:52:9b:
+                    c7:e5
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                49:EA:B2:99:05:EC:D6:35:F4:11:A5:DD:F1:99:31:AE:C0:D8:B8:08
+            X509v3 Authority Key Identifier: 
+                keyid:7C:05:F1:0D:0B:C4:F8:8E:33:4C:F1:AE:78:6A:2C:16:AD:79:D0:A6
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+    Signature Algorithm: sha256WithRSAEncryption
+         81:53:9e:26:eb:d2:fc:1c:8b:d7:28:cd:57:e4:54:ac:c6:f4:
+         ed:66:1e:a4:8d:52:6f:2d:bd:91:e1:10:b5:92:53:26:32:a2:
+         de:19:34:6b:b3:af:ab:87:71:ed:4a:5f:51:db:0d:51:b5:33:
+         b8:99:ca:c3:ac:2b:82:64:bb:44:5d:df:cb:69:04:f1:b4:08:
+         af:fa:89:c3:69:44:25:f5:fa:1e:29:77:a9:dd:db:27:18:20:
+         fa:7a:5a:33:55:e4:cc:83:05:d4:72:8c:1d:e7:34:42:4d:9c:
+         cc:7e:7c:54:35:8f:db:be:dd:36:84:01:7c:e1:e1:b0:79:ef:
+         b0:6c:9a:27:c8:f2:e5:dc:48:57:15:da:d4:31:d9:8c:f7:38:
+         03:40:85:8f:23:06:87:2b:ee:45:8f:24:52:15:0c:89:d2:80:
+         74:5c:9f:58:d9:68:b4:10:fe:56:78:19:4d:a6:8e:56:46:35:
+         65:1f:29:2e:29:7b:9b:24:83:7a:e9:e9:7e:02:4c:4a:40:56:
+         77:2c:ed:dd:d2:ae:0c:9d:9c:b2:5b:88:b5:8a:df:ac:55:b0:
+         ad:e1:cf:63:8d:65:c7:bd:2b:15:dd:34:35:29:cb:9e:2d:00:
+         92:55:eb:d9:71:6d:29:82:8e:be:3f:93:cf:b5:de:43:4f:c0:
+         7d:94:96:29
+-----BEGIN CERTIFICATE-----
+MIIDbjCCAlagAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAnqV3
+cCi8jxjOojllgRjZMQruIwmBzYI2mKWCLrH8WahGSQYWYOsSRiyFqMB1ey1FDwm6
+1qn9iA/Wl3geXVHMGpQeLriCTuMMqlgbxt390qOSbAquXspWh5psvYkymVvaM916
+7afF4QLeJVsXavUVijFxx8AkfFXEjs9MA/1/9OGQzBpetfFLLnovaQYqhnYziO+y
+4IRSmAPrmHsWJWVqplr6N7WhcHRelodieKTUBWCXOvlMhkkNVNt5jWhWWIPujxra
+1zoGeG518KmZOiKjBu5YoivR6kqnisi9Q/ixcE/7UbYieM9cxtyAMz31km2RyRt+
+hzcodrT/f21Sm8flAgMBAAGjgcowgccwHQYDVR0OBBYEFEnqspkF7NY19BGl3fGZ
+Ma7A2LgIMB8GA1UdIwQYMBaAFHwF8Q0LxPiOM0zxrnhqLBatedCmMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBCwUAA4IB
+AQCBU54m69L8HIvXKM1X5FSsxvTtZh6kjVJvLb2R4RC1klMmMqLeGTRrs6+rh3Ht
+Sl9R2w1RtTO4mcrDrCuCZLtEXd/LaQTxtAiv+onDaUQl9foeKXep3dsnGCD6eloz
+VeTMgwXUcowd5zRCTZzMfnxUNY/bvt02hAF84eGwee+wbJonyPLl3EhXFdrUMdmM
+9zgDQIWPIwaHK+5FjyRSFQyJ0oB0XJ9Y2Wi0EP5WeBlNpo5WRjVlHykuKXubJIN6
+6el+AkxKQFZ3LO3d0q4MnZyyW4i1it+sVbCt4c9jjWXHvSsV3TQ1KcueLQCSVevZ
+cW0pgo6+P5PPtd5DT8B9lJYp
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:96:3d:06:43:c1:e6:7e:de:e8:e7:d0:e0:3e:d5:
+                    a8:1f:08:fe:98:ef:b9:a3:3d:0f:14:a5:3e:85:27:
+                    6a:a2:aa:8c:3b:f6:21:43:12:2d:c8:9c:b3:cf:a8:
+                    ee:4f:8c:b7:3f:7f:49:49:a3:dc:17:3b:4b:28:2a:
+                    93:ae:e1:df:7e:22:b2:1d:9c:5f:43:59:a5:4e:55:
+                    dd:db:55:46:55:83:6d:48:76:ef:3f:77:24:58:d3:
+                    7b:78:28:05:7c:66:7c:79:90:99:61:b6:cc:2d:b6:
+                    ef:36:ca:11:3b:b5:65:c8:73:91:b0:10:12:17:a3:
+                    e1:1c:ea:c7:dc:9c:b3:66:3d:ec:bb:a3:3e:99:e0:
+                    04:98:4d:9c:b5:62:ad:16:71:22:00:68:ef:e6:42:
+                    f6:05:28:4d:88:16:05:51:82:f6:d8:83:91:0c:13:
+                    4c:3c:6e:d6:22:2a:52:da:37:56:a9:24:18:ba:8f:
+                    2e:65:d9:3a:4b:e0:a3:69:94:3b:16:5e:4a:cc:a8:
+                    6e:32:6d:f8:74:15:32:c8:9f:af:06:11:81:db:9a:
+                    65:cf:01:05:2f:65:4b:71:4e:92:1c:06:51:7f:29:
+                    57:e9:24:9f:89:f9:80:63:25:97:90:42:56:f8:e1:
+                    26:61:b2:48:b3:20:9b:9a:9b:34:4e:2f:03:06:a7:
+                    dc:11
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                7C:05:F1:0D:0B:C4:F8:8E:33:4C:F1:AE:78:6A:2C:16:AD:79:D0:A6
+            X509v3 Authority Key Identifier: 
+                keyid:F2:26:B1:CF:39:F4:1E:77:A5:A1:DA:65:4B:C4:D0:12:C9:53:25:AE
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         67:35:59:8e:50:f3:67:61:84:47:e2:66:a4:1a:96:63:91:79:
+         f6:80:f7:23:ea:2c:de:22:ca:1f:4a:4b:a9:c2:80:54:8d:d9:
+         e0:bf:c7:e6:38:cf:66:b5:8a:a9:7c:25:bf:39:f5:16:1f:de:
+         4d:52:01:67:69:7a:e6:92:a0:98:20:e6:be:bc:f8:a5:8d:46:
+         02:a8:6b:13:69:97:ee:ac:46:a4:36:cc:b2:9c:af:2a:0b:18:
+         87:48:22:a9:d7:87:5d:5f:50:35:e0:ab:3d:1f:2f:88:56:58:
+         e0:7b:3c:38:21:72:23:dc:6c:e7:c9:83:59:e1:c9:a7:44:bf:
+         40:3e:3b:00:a5:8a:44:d2:ee:66:37:f9:b4:a0:18:bf:39:9f:
+         76:3e:4c:cd:95:02:c7:44:f6:21:f0:42:15:8a:0b:0d:0f:94:
+         87:e2:d9:47:d4:33:0e:a3:fe:b4:a2:92:19:9d:8e:52:3c:aa:
+         d3:0f:6c:1b:2c:af:7f:c4:16:6f:13:b7:53:ee:dd:e4:1b:06:
+         f0:c9:8b:44:1e:cc:9c:85:d9:d7:b8:59:7a:31:71:46:dc:48:
+         4d:bf:6b:37:fb:a0:89:f0:96:b3:96:07:f6:62:a5:90:75:32:
+         0e:39:31:f0:9f:53:a7:01:0b:07:62:ba:6f:7b:93:4d:57:9a:
+         b5:2d:22:ff
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlj0GQ8Hm
+ft7o59DgPtWoHwj+mO+5oz0PFKU+hSdqoqqMO/YhQxItyJyzz6juT4y3P39JSaPc
+FztLKCqTruHffiKyHZxfQ1mlTlXd21VGVYNtSHbvP3ckWNN7eCgFfGZ8eZCZYbbM
+LbbvNsoRO7VlyHORsBASF6PhHOrH3JyzZj3su6M+meAEmE2ctWKtFnEiAGjv5kL2
+BShNiBYFUYL22IORDBNMPG7WIipS2jdWqSQYuo8uZdk6S+CjaZQ7Fl5KzKhuMm34
+dBUyyJ+vBhGB25plzwEFL2VLcU6SHAZRfylX6SSfifmAYyWXkEJW+OEmYbJIsyCb
+mps0Ti8DBqfcEQIDAQABo4HLMIHIMB0GA1UdDgQWBBR8BfENC8T4jjNM8a54aiwW
+rXnQpjAfBgNVHSMEGDAWgBTyJrHPOfQed6Wh2mVLxNASyVMlrjA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AGc1WY5Q82dhhEfiZqQalmORefaA9yPqLN4iyh9KS6nCgFSN2eC/x+Y4z2a1iql8
+Jb859RYf3k1SAWdpeuaSoJgg5r68+KWNRgKoaxNpl+6sRqQ2zLKcryoLGIdIIqnX
+h11fUDXgqz0fL4hWWOB7PDghciPcbOfJg1nhyadEv0A+OwClikTS7mY3+bSgGL85
+n3Y+TM2VAsdE9iHwQhWKCw0PlIfi2UfUMw6j/rSikhmdjlI8qtMPbBssr3/EFm8T
+t1Pu3eQbBvDJi0QezJyF2de4WXoxcUbcSE2/azf7oInwlrOWB/ZipZB1Mg45MfCf
+U6cBCwdium97k01XmrUtIv8=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:cd:e5:09:a9:89:90:93:c8:33:6e:5b:db:e8:8e:
+                    12:b1:4a:dd:a2:3a:60:ad:e7:26:98:bc:5f:63:12:
+                    8d:39:97:38:53:96:ef:65:d9:43:06:fb:e7:1f:8d:
+                    ac:32:87:84:ae:50:cc:8f:48:fa:b2:e2:7e:58:32:
+                    2c:78:a0:c6:82:41:3c:4d:a1:01:43:0f:c4:a7:9d:
+                    5d:24:16:39:b4:13:7b:09:b1:dc:3f:e5:ac:3b:d7:
+                    53:b6:59:52:80:87:b2:12:65:6c:9c:fe:24:92:88:
+                    ca:5a:ec:e4:04:81:a4:88:b2:3f:39:e1:4d:6b:91:
+                    db:17:76:c3:0e:67:a4:5d:c8:e4:76:16:44:f7:76:
+                    ae:db:63:7f:37:70:d0:e5:fc:df:08:0f:2e:f9:08:
+                    72:f6:65:4f:af:15:97:a3:4c:03:f9:8f:5f:69:f8:
+                    97:d8:dd:fe:e7:2c:1d:a3:d8:53:46:df:5c:c5:8e:
+                    d8:38:41:ce:d7:ea:7f:f1:3b:0c:dd:13:dd:e7:2f:
+                    44:24:aa:25:e3:eb:a1:8e:43:b1:5b:e3:b8:ad:aa:
+                    1e:49:f0:77:40:64:ef:90:ad:72:a4:a0:d3:95:69:
+                    96:4b:6d:08:34:97:cc:5e:5c:a5:08:c9:fa:66:60:
+                    ec:f9:aa:86:d8:bc:21:b7:78:bf:f2:28:01:41:3d:
+                    a4:9d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                F2:26:B1:CF:39:F4:1E:77:A5:A1:DA:65:4B:C4:D0:12:C9:53:25:AE
+            X509v3 Authority Key Identifier: 
+                keyid:F2:26:B1:CF:39:F4:1E:77:A5:A1:DA:65:4B:C4:D0:12:C9:53:25:AE
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         7d:7d:1b:68:17:cb:34:c5:15:3a:84:51:3b:d5:d2:66:a5:08:
+         0d:66:b0:a3:14:7a:28:4f:88:44:1e:a0:0a:db:77:c9:f0:72:
+         c2:63:2c:2c:75:1a:64:dc:ca:f8:93:38:8f:86:17:40:d1:1f:
+         99:97:91:6b:1d:e2:c6:60:21:bb:5c:46:d3:be:e2:22:4d:f8:
+         87:24:78:c0:94:70:98:dc:c2:8d:c0:1b:4c:65:c4:6b:89:c2:
+         bc:da:8f:39:91:b3:74:78:75:ca:cf:21:82:5e:6b:ae:97:30:
+         26:47:fb:33:32:19:ec:88:4f:24:28:cb:d0:22:de:21:cd:f4:
+         cd:11:53:ce:ca:0f:55:a0:a5:74:a6:59:7e:e0:9d:b6:79:26:
+         a1:3a:48:2b:de:56:af:b8:62:be:24:10:be:c3:fa:f3:c5:9f:
+         7f:1e:a7:fe:99:5d:aa:30:0f:f6:61:29:1d:a1:8f:9e:97:4f:
+         f7:92:a2:ab:a3:1d:76:5a:f1:c7:de:b7:b9:da:61:69:69:4e:
+         f4:5a:69:b2:0f:5e:6b:4a:4a:52:68:08:bf:ef:61:9b:06:3e:
+         93:82:cd:af:30:f1:d9:c6:d7:cf:a6:0b:b4:76:a1:72:48:de:
+         f9:6c:8a:28:58:56:10:ed:d9:67:cf:a0:77:9c:e6:2d:ba:36:
+         31:ef:5c:a8
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM3lCamJkJPIM25b2+iO
+ErFK3aI6YK3nJpi8X2MSjTmXOFOW72XZQwb75x+NrDKHhK5QzI9I+rLiflgyLHig
+xoJBPE2hAUMPxKedXSQWObQTewmx3D/lrDvXU7ZZUoCHshJlbJz+JJKIylrs5ASB
+pIiyPznhTWuR2xd2ww5npF3I5HYWRPd2rttjfzdw0OX83wgPLvkIcvZlT68Vl6NM
+A/mPX2n4l9jd/ucsHaPYU0bfXMWO2DhBztfqf/E7DN0T3ecvRCSqJeProY5DsVvj
+uK2qHknwd0Bk75CtcqSg05VplkttCDSXzF5cpQjJ+mZg7Pmqhti8Ibd4v/IoAUE9
+pJ0CAwEAAaOByzCByDAdBgNVHQ4EFgQU8iaxzzn0HnelodplS8TQEslTJa4wHwYD
+VR0jBBgwFoAU8iaxzzn0HnelodplS8TQEslTJa4wNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB9fRtoF8s0
+xRU6hFE71dJmpQgNZrCjFHooT4hEHqAK23fJ8HLCYywsdRpk3Mr4kziPhhdA0R+Z
+l5FrHeLGYCG7XEbTvuIiTfiHJHjAlHCY3MKNwBtMZcRricK82o85kbN0eHXKzyGC
+XmuulzAmR/szMhnsiE8kKMvQIt4hzfTNEVPOyg9VoKV0pll+4J22eSahOkgr3lav
+uGK+JBC+w/rzxZ9/Hqf+mV2qMA/2YSkdoY+el0/3kqKrox12WvHH3re52mFpaU70
+WmmyD15rSkpSaAi/72GbBj6Tgs2vMPHZxtfPpgu0dqFySN75bIooWFYQ7dlnz6B3
+nOYtujYx71yo
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+SUCCESS
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
diff --git a/net/data/verify_certificate_chain_unittest/target-restricts-eku-fail.pem b/net/data/verify_certificate_chain_unittest/target-restricts-eku-fail.pem
new file mode 100644
index 0000000..8771bab
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/target-restricts-eku-fail.pem
@@ -0,0 +1,295 @@
+[Created by: generate-target-restricts-eku-fail.py]
+
+Certificate chain with 1 intermediate and a trusted root. The target
+certificate has only clientAuth EKU, so is expected to fail when verifying for
+serverAuth.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:e2:eb:a5:10:98:bb:69:c3:14:d9:d6:c6:57:1a:
+                    e0:9a:9e:30:b5:d8:b7:e3:fd:4a:ef:08:01:b0:b8:
+                    94:0a:0a:c6:24:5e:3f:50:d1:e7:21:f7:ce:0c:5f:
+                    af:a1:ab:6e:35:d4:56:cf:bd:45:fb:6e:35:5d:0f:
+                    34:7e:36:61:ab:f2:dd:4b:d5:23:80:5d:55:77:fc:
+                    fb:0e:2c:8d:86:13:e1:eb:88:9f:69:f0:45:19:2f:
+                    94:9e:fa:2c:e0:fc:a3:23:8c:8d:cf:f0:7e:65:9f:
+                    2b:fc:34:04:75:b8:56:a9:19:35:f7:d2:3f:2b:f0:
+                    cf:1a:76:dd:da:08:31:9b:4c:b1:83:31:32:2f:ab:
+                    1c:61:51:25:b9:d7:11:3b:fa:7f:8b:1b:c9:76:56:
+                    76:d5:07:f6:95:99:db:3e:e0:e5:5b:04:60:93:d3:
+                    4a:0b:4e:06:66:48:a2:93:de:7e:3c:2f:06:38:b2:
+                    18:6f:b1:d8:1f:a1:10:16:06:d0:a1:67:cf:e2:7d:
+                    e0:17:c1:b2:95:6c:34:b2:74:33:ec:eb:71:5e:ac:
+                    25:b2:80:1f:c6:a5:ea:ae:e2:62:72:18:33:53:0e:
+                    44:57:93:e0:cd:3a:23:f3:0b:e9:9a:be:ac:0c:57:
+                    7b:51:76:24:24:28:38:8e:0d:c8:84:ac:31:49:87:
+                    79:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                49:89:8E:36:AD:53:F8:AC:67:4F:B7:A4:DC:48:2F:19:41:7D:33:DE
+            X509v3 Authority Key Identifier: 
+                keyid:22:82:C0:6D:04:2C:68:BF:9A:C0:A0:64:5C:CA:16:43:09:45:40:B3
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         1e:8b:4a:94:5f:97:89:86:d3:39:07:51:b0:85:ae:a4:ef:68:
+         d4:02:a1:cc:97:55:10:05:bd:73:de:50:a5:3e:35:0d:3e:f1:
+         a0:2c:2c:9d:36:9e:93:c9:04:ef:aa:c2:3e:71:87:b9:2a:fb:
+         45:5d:73:89:37:35:33:9a:1d:26:6a:c1:43:f4:c2:6e:96:48:
+         83:26:6b:29:1c:d0:17:b9:68:93:7e:30:86:ad:82:07:27:85:
+         74:d0:7a:5d:2c:de:69:f3:6d:9b:07:34:2c:b0:00:fd:28:1a:
+         79:f0:15:00:c7:d0:72:4e:9d:20:b5:c3:a5:6f:a7:51:16:70:
+         63:1a:14:53:38:72:24:ae:a2:7f:bc:84:9a:66:85:7d:8e:17:
+         d0:b0:62:9d:77:66:30:61:5c:43:f1:2a:05:4e:c6:d4:51:a1:
+         23:71:e5:e1:22:02:44:0b:36:ec:d7:8c:20:13:97:38:ec:96:
+         2e:f1:15:7e:22:96:41:25:8c:6f:35:f1:08:33:5b:f4:66:67:
+         ee:03:1f:d7:76:d1:16:d3:50:6f:8a:56:cd:e6:7c:ca:43:b2:
+         39:f5:ac:42:c8:e5:b7:94:9a:1d:32:81:6d:39:eb:00:5a:5a:
+         2c:4d:85:73:ef:62:2f:a8:88:9a:df:26:83:fc:d1:2e:a6:fc:
+         70:93:33:4e
+-----BEGIN CERTIFICATE-----
+MIIDgzCCAmugAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDi66UQ
+mLtpwxTZ1sZXGuCanjC12Lfj/UrvCAGwuJQKCsYkXj9Q0ech984MX6+hq2411FbP
+vUX7bjVdDzR+NmGr8t1L1SOAXVV3/PsOLI2GE+HriJ9p8EUZL5Se+izg/KMjjI3P
+8H5lnyv8NAR1uFapGTX30j8r8M8adt3aCDGbTLGDMTIvqxxhUSW51xE7+n+LG8l2
+VnbVB/aVmds+4OVbBGCT00oLTgZmSKKT3n48LwY4shhvsdgfoRAWBtChZ8/ifeAX
+wbKVbDSydDPs63FerCWygB/Gpequ4mJyGDNTDkRXk+DNOiPzC+mavqwMV3tRdiQk
+KDiODciErDFJh3kNAgMBAAGjgd8wgdwwHQYDVR0OBBYEFEmJjjatU/isZ0+3pNxI
+LxlBfTPeMB8GA1UdIwQYMBaAFCKCwG0ELGi/msCgZFzKFkMJRUCzMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF
+BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAei0qUX5eJhtM5B1Gwha6k72jUAqHMl1UQ
+Bb1z3lClPjUNPvGgLCydNp6TyQTvqsI+cYe5KvtFXXOJNzUzmh0masFD9MJulkiD
+JmspHNAXuWiTfjCGrYIHJ4V00HpdLN5p822bBzQssAD9KBp58BUAx9ByTp0gtcOl
+b6dRFnBjGhRTOHIkrqJ/vISaZoV9jhfQsGKdd2YwYVxD8SoFTsbUUaEjceXhIgJE
+Czbs14wgE5c47JYu8RV+IpZBJYxvNfEIM1v0ZmfuAx/XdtEW01BvilbN5nzKQ7I5
+9axCyOW3lJodMoFtOesAWlosTYVz72IvqIia3yaD/NEupvxwkzNO
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:9a:8b:44:57:fb:d0:44:24:d6:20:38:aa:1b:20:
+                    cc:67:40:26:c1:98:2a:6e:95:cf:db:2c:3b:a6:c9:
+                    e6:58:06:01:c5:d7:2d:3a:0d:f5:45:92:6c:d7:16:
+                    78:5b:3a:1d:16:f7:3c:c9:9b:1a:95:17:f7:cd:30:
+                    75:44:c4:bd:42:ed:b2:6b:e6:9b:a8:4e:2e:cf:36:
+                    2e:3e:c3:c0:21:df:ed:b2:05:ca:65:59:12:c9:2a:
+                    3d:bf:3a:df:5e:17:f6:eb:a8:78:e6:81:c7:d6:5e:
+                    ac:9b:e9:aa:c5:af:6f:d2:04:08:4a:9e:9b:02:68:
+                    40:b3:ca:8d:5e:cf:48:4d:fd:44:fe:8d:15:19:ab:
+                    fd:fb:7e:32:34:c9:90:15:b2:8e:6b:4a:9c:63:68:
+                    85:bb:91:bb:1b:cb:8b:c1:6f:06:32:67:4d:0d:f3:
+                    9a:ab:6c:80:f3:79:f1:ac:bb:48:2a:e2:ef:af:8a:
+                    3e:86:8a:72:3e:df:ad:7a:ad:90:50:e4:65:69:37:
+                    26:4a:16:ce:b6:11:9c:36:49:da:85:da:af:5f:91:
+                    d6:a7:94:3a:af:96:6f:6f:4e:01:a3:51:06:e7:7a:
+                    ab:41:a3:17:21:b6:9d:a6:aa:75:ff:06:7b:fa:e8:
+                    77:5a:58:af:1d:47:b5:c8:8b:bf:c2:a1:6a:4c:01:
+                    82:d7
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                22:82:C0:6D:04:2C:68:BF:9A:C0:A0:64:5C:CA:16:43:09:45:40:B3
+            X509v3 Authority Key Identifier: 
+                keyid:DE:2B:DF:9E:08:76:11:61:90:16:9D:68:25:D2:F9:40:1B:36:70:3D
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         67:75:dd:21:1d:a3:c1:e9:98:a8:b8:28:e6:c4:d4:94:23:32:
+         67:d8:44:ee:37:ce:a4:1f:6b:48:f5:8e:0f:51:a1:0c:83:d0:
+         cb:ee:7f:24:b7:bc:e6:a9:e1:75:ad:ae:ec:e0:26:3e:79:85:
+         f9:ae:2a:45:3a:dc:69:a2:06:6a:36:89:9e:fe:df:19:74:3f:
+         cb:70:7a:d1:7e:53:77:f3:fb:c4:b9:08:dc:4f:13:e3:3f:23:
+         b9:8b:0d:d7:b5:fd:4c:3b:30:ee:f6:b3:d0:fc:51:a2:f0:62:
+         76:ae:ba:ec:9b:a5:c9:14:e3:40:9c:f8:4f:38:ef:8d:3b:be:
+         eb:09:d8:34:fb:42:1b:07:8f:2a:b2:93:ff:f3:9f:e4:84:0b:
+         c5:54:2b:b4:a8:66:47:20:2a:97:25:fc:ca:64:12:61:7c:2a:
+         d2:a7:9f:e6:0c:50:0f:3e:bd:fb:a5:4a:ed:94:96:7c:48:f9:
+         6f:34:d4:2f:e3:21:e8:f8:93:f0:01:ae:1c:1d:73:2a:99:fc:
+         f6:ab:0c:55:ae:9d:63:94:b8:1d:0a:0c:a7:47:4f:aa:d3:a7:
+         69:17:4d:6c:1c:a3:c5:bd:f8:78:24:35:1d:63:8d:ca:15:d4:
+         01:71:85:0e:7c:02:c9:5f:26:b8:55:c3:1b:63:fb:da:88:41:
+         4c:22:6d:37
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmotEV/vQ
+RCTWIDiqGyDMZ0AmwZgqbpXP2yw7psnmWAYBxdctOg31RZJs1xZ4WzodFvc8yZsa
+lRf3zTB1RMS9Qu2ya+abqE4uzzYuPsPAId/tsgXKZVkSySo9vzrfXhf266h45oHH
+1l6sm+mqxa9v0gQISp6bAmhAs8qNXs9ITf1E/o0VGav9+34yNMmQFbKOa0qcY2iF
+u5G7G8uLwW8GMmdNDfOaq2yA83nxrLtIKuLvr4o+hopyPt+teq2QUORlaTcmShbO
+thGcNknahdqvX5HWp5Q6r5Zvb04Bo1EG53qrQaMXIbadpqp1/wZ7+uh3WlivHUe1
+yIu/wqFqTAGC1wIDAQABo4HLMIHIMB0GA1UdDgQWBBQigsBtBCxov5rAoGRcyhZD
+CUVAszAfBgNVHSMEGDAWgBTeK9+eCHYRYZAWnWgl0vlAGzZwPTA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AGd13SEdo8HpmKi4KObE1JQjMmfYRO43zqQfa0j1jg9RoQyD0MvufyS3vOap4XWt
+ruzgJj55hfmuKkU63GmiBmo2iZ7+3xl0P8twetF+U3fz+8S5CNxPE+M/I7mLDde1
+/Uw7MO72s9D8UaLwYnauuuybpckU40Cc+E847407vusJ2DT7QhsHjyqyk//zn+SE
+C8VUK7SoZkcgKpcl/MpkEmF8KtKnn+YMUA8+vfulSu2UlnxI+W801C/jIej4k/AB
+rhwdcyqZ/ParDFWunWOUuB0KDKdHT6rTp2kXTWwco8W9+HgkNR1jjcoV1AFxhQ58
+AslfJrhVwxtj+9qIQUwibTc=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a9:86:82:11:83:72:a2:7a:5a:86:42:58:ae:80:
+                    ca:dc:fd:62:f0:1a:ad:b4:1e:65:34:18:32:1b:10:
+                    3a:61:60:57:5a:41:76:35:c8:34:9c:e7:ac:dd:a8:
+                    20:60:ec:0c:bb:ca:fc:de:bb:55:b2:10:49:9e:50:
+                    ef:67:d0:4c:2b:66:26:25:ac:48:fa:86:2e:86:30:
+                    66:3b:8b:d3:cd:09:d4:4c:19:e5:7e:01:f9:8d:29:
+                    b3:58:09:a4:4f:c1:6f:a5:19:0d:f3:7d:22:7c:74:
+                    91:ab:b2:39:b4:6a:7a:a2:cb:99:05:d9:f2:f4:4a:
+                    39:47:25:54:2e:b7:3e:df:27:8e:b1:96:04:30:c2:
+                    aa:70:46:d6:96:23:47:5f:34:40:ed:42:11:ac:51:
+                    a3:d1:a0:c5:17:19:33:5c:e4:53:26:8b:a6:21:b2:
+                    87:e3:43:3f:8e:5a:04:04:62:5a:41:8d:bf:c7:c4:
+                    62:ed:bb:7a:00:9b:60:de:00:2a:5d:b2:40:af:00:
+                    a2:9d:28:5a:b1:d9:5a:4d:82:e9:9c:b2:6d:54:16:
+                    95:26:cf:2e:5a:69:86:9e:40:20:d9:42:6e:3f:c7:
+                    a1:1d:40:c0:19:ea:15:77:93:ce:02:b2:cb:c9:15:
+                    8a:9c:49:2b:65:07:97:bd:db:00:0d:b2:1e:bd:b3:
+                    0e:55
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                DE:2B:DF:9E:08:76:11:61:90:16:9D:68:25:D2:F9:40:1B:36:70:3D
+            X509v3 Authority Key Identifier: 
+                keyid:DE:2B:DF:9E:08:76:11:61:90:16:9D:68:25:D2:F9:40:1B:36:70:3D
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         8d:82:b5:9e:ac:db:05:8b:4b:54:e9:e8:3c:67:2d:7d:e9:9b:
+         cf:ad:3e:78:a7:db:c2:68:cf:a7:df:15:88:f3:eb:60:5f:09:
+         e1:9d:e9:c7:44:19:2a:86:53:57:b6:1a:b4:dc:7f:32:eb:29:
+         28:3b:74:fe:33:ee:fa:85:c1:0b:43:c3:3f:7a:c2:19:05:9a:
+         27:73:43:3c:03:9f:10:dc:d1:3e:6d:b2:8c:95:d5:5b:cc:62:
+         96:51:f8:1c:6a:4c:6c:9d:8a:47:8e:12:08:de:30:0d:b1:4f:
+         b3:f6:95:9a:fc:16:e3:5a:b2:7f:93:09:3f:e1:59:f6:60:e2:
+         56:22:7c:24:cd:67:9f:bc:a5:c7:10:50:03:92:54:04:d7:f8:
+         3b:a2:ae:ca:23:21:f2:90:9f:c6:66:0f:62:49:2d:aa:be:8d:
+         3a:e3:e7:3c:0a:16:48:dc:11:e0:74:9d:11:d8:ce:98:95:7c:
+         99:a5:7b:a5:3a:3b:3e:e9:29:dd:4a:09:88:a0:ef:6c:a1:bf:
+         8e:46:07:01:ed:93:fd:64:c9:15:b0:8c:e5:ce:23:9b:22:b4:
+         93:48:b4:19:04:a6:18:8b:03:11:dd:d0:3a:ff:32:62:da:c4:
+         f0:37:1a:7a:9c:ba:67:6d:bd:a0:b1:13:ea:54:58:78:8c:b8:
+         f7:91:a7:7a
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKmGghGDcqJ6WoZCWK6A
+ytz9YvAarbQeZTQYMhsQOmFgV1pBdjXINJznrN2oIGDsDLvK/N67VbIQSZ5Q72fQ
+TCtmJiWsSPqGLoYwZjuL080J1EwZ5X4B+Y0ps1gJpE/Bb6UZDfN9Inx0kauyObRq
+eqLLmQXZ8vRKOUclVC63Pt8njrGWBDDCqnBG1pYjR180QO1CEaxRo9GgxRcZM1zk
+UyaLpiGyh+NDP45aBARiWkGNv8fEYu27egCbYN4AKl2yQK8Aop0oWrHZWk2C6Zyy
+bVQWlSbPLlpphp5AINlCbj/HoR1AwBnqFXeTzgKyy8kVipxJK2UHl73bAA2yHr2z
+DlUCAwEAAaOByzCByDAdBgNVHQ4EFgQU3ivfngh2EWGQFp1oJdL5QBs2cD0wHwYD
+VR0jBBgwFoAU3ivfngh2EWGQFp1oJdL5QBs2cD0wNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCNgrWerNsF
+i0tU6eg8Zy196ZvPrT54p9vCaM+n3xWI8+tgXwnhnenHRBkqhlNXthq03H8y6yko
+O3T+M+76hcELQ8M/esIZBZonc0M8A58Q3NE+bbKMldVbzGKWUfgcakxsnYpHjhII
+3jANsU+z9pWa/BbjWrJ/kwk/4Vn2YOJWInwkzWefvKXHEFADklQE1/g7oq7KIyHy
+kJ/GZg9iSS2qvo064+c8ChZI3BHgdJ0R2M6YlXyZpXulOjs+6SndSgmIoO9sob+O
+RgcB7ZP9ZMkVsIzlziObIrSTSLQZBKYYiwMR3dA6/zJi2sTwNxp6nLpnbb2gsRPq
+VFh4jLj3kad6
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+FAIL
+-----BEGIN VERIFY_RESULT-----
+RkFJTA==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
+
+----- Certificate i=0 (CN=Target) -----
+ERROR: The extended key usage does not include server auth
+
+
+-----BEGIN ERRORS-----
+LS0tLS0gQ2VydGlmaWNhdGUgaT0wIChDTj1UYXJnZXQpIC0tLS0tCkVSUk9SOiBUaGUgZXh0ZW5kZWQga2V5IHVzYWdlIGRvZXMgbm90IGluY2x1ZGUgc2VydmVyIGF1dGgKCg==
+-----END ERRORS-----
diff --git a/net/data/verify_certificate_chain_unittest/target-sets-eku-any.pem b/net/data/verify_certificate_chain_unittest/target-sets-eku-any.pem
new file mode 100644
index 0000000..c779a18
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/target-sets-eku-any.pem
@@ -0,0 +1,287 @@
+[Created by: generate-target-sets-eku-any.py]
+
+Certificate chain with 1 intermediate and a trusted root. The target
+restricts EKU to clientAuth+any and requests serverAuth during verification.
+This should succeed.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c3:82:13:64:0e:35:33:0c:ac:44:be:6d:92:f5:
+                    e4:97:d8:9a:bd:64:f1:b5:67:62:01:7b:0c:98:57:
+                    4a:63:64:b0:9d:6a:7b:84:a2:91:fe:73:0b:4c:81:
+                    ce:89:f9:8d:8d:8a:41:18:c8:d8:64:27:36:32:e6:
+                    36:26:44:16:13:2e:a1:ad:38:06:0b:1b:39:62:6a:
+                    94:ac:a0:59:be:52:cb:47:d7:4b:00:09:91:8e:14:
+                    69:a9:62:df:49:d8:b6:79:73:de:60:d4:b8:76:89:
+                    a4:53:8a:1d:4b:80:88:31:e8:05:46:81:1b:7b:5d:
+                    52:d0:6b:3b:53:0d:25:3c:95:9b:2d:99:83:3c:03:
+                    8c:b5:73:fb:43:6c:82:b3:48:57:38:3c:ff:b7:79:
+                    d8:13:74:06:d0:17:78:a9:38:09:76:ca:f9:b7:5a:
+                    a5:8a:6e:85:7f:27:34:79:82:ef:a2:01:93:ae:fa:
+                    0b:18:47:d4:14:ff:67:78:2b:53:92:f6:ac:27:42:
+                    c7:7f:8e:fd:06:4a:36:b9:7a:98:5e:0d:94:ef:1a:
+                    fa:08:ad:8d:64:28:c7:c1:03:76:63:b9:33:5a:9f:
+                    16:be:d3:e0:5c:e9:43:7b:9b:83:b3:90:31:e7:59:
+                    2b:1c:d2:8c:73:15:a2:3a:94:35:03:80:97:f8:5d:
+                    a3:13
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                5A:16:9C:06:85:B6:F4:77:AD:72:58:A2:4F:A1:FE:29:CF:97:8A:2B
+            X509v3 Authority Key Identifier: 
+                keyid:24:B9:91:41:39:F1:30:5E:F8:C5:3B:C0:51:CC:11:58:A6:13:73:B3
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication, Any Extended Key Usage
+    Signature Algorithm: sha256WithRSAEncryption
+         e9:be:ed:82:c2:ea:51:5e:9d:c9:4f:2c:e3:ca:6e:0b:be:18:
+         5c:c3:1e:3f:6c:1a:de:9d:cc:8c:45:b5:87:2e:b2:38:9a:84:
+         a6:e1:ef:93:ac:f1:b6:d0:ba:d6:87:59:5d:e8:a7:1b:3e:14:
+         3a:9d:0b:50:c3:29:51:65:62:66:9e:a2:68:e8:be:f0:1a:e2:
+         57:0e:dc:7e:28:94:d9:c3:a9:0e:9c:d6:b0:76:d8:ec:b9:fe:
+         e6:35:b5:8a:27:3f:1d:6a:4e:66:d5:ac:d8:05:b2:4d:47:16:
+         ff:09:88:7e:23:70:dc:6b:b2:14:38:97:d8:4c:5d:ee:41:aa:
+         ac:b3:6b:5f:d7:2f:39:93:19:5f:e6:b7:15:c9:2b:5b:2c:c0:
+         b6:81:84:49:0b:5f:df:e9:e1:01:4f:82:ad:35:0b:00:d3:ff:
+         47:55:67:20:aa:3e:f9:b0:84:09:8d:e0:7b:16:b0:11:a5:16:
+         a7:27:81:85:ec:2d:47:73:48:e4:92:9d:b0:81:27:32:28:89:
+         de:cb:c2:fb:bd:60:09:2e:9a:99:ef:9b:cb:9c:2b:fc:b5:a2:
+         cc:01:73:bd:42:28:00:d4:d8:b2:8d:94:6b:5e:bf:1e:8e:93:
+         13:89:65:6b:2f:af:92:37:a3:b4:98:14:f4:b1:ff:44:aa:c1:
+         79:83:48:f7
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDghNk
+DjUzDKxEvm2S9eSX2Jq9ZPG1Z2IBewyYV0pjZLCdanuEopH+cwtMgc6J+Y2NikEY
+yNhkJzYy5jYmRBYTLqGtOAYLGzliapSsoFm+UstH10sACZGOFGmpYt9J2LZ5c95g
+1Lh2iaRTih1LgIgx6AVGgRt7XVLQaztTDSU8lZstmYM8A4y1c/tDbIKzSFc4PP+3
+edgTdAbQF3ipOAl2yvm3WqWKboV/JzR5gu+iAZOu+gsYR9QU/2d4K1OS9qwnQsd/
+jv0GSja5epheDZTvGvoIrY1kKMfBA3ZjuTNanxa+0+Bc6UN7m4OzkDHnWSsc0oxz
+FaI6lDUDgJf4XaMTAgMBAAGjgeUwgeIwHQYDVR0OBBYEFFoWnAaFtvR3rXJYok+h
+/inPl4orMB8GA1UdIwQYMBaAFCS5kUE58TBe+MU7wFHMEVimE3OzMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMBkGA1UdJQQSMBAGCCsGAQUF
+BwMCBgRVHSUAMA0GCSqGSIb3DQEBCwUAA4IBAQDpvu2CwupRXp3JTyzjym4Lvhhc
+wx4/bBrencyMRbWHLrI4moSm4e+TrPG20LrWh1ld6KcbPhQ6nQtQwylRZWJmnqJo
+6L7wGuJXDtx+KJTZw6kOnNawdtjsuf7mNbWKJz8dak5m1azYBbJNRxb/CYh+I3Dc
+a7IUOJfYTF3uQaqss2tf1y85kxlf5rcVyStbLMC2gYRJC1/f6eEBT4KtNQsA0/9H
+VWcgqj75sIQJjeB7FrARpRanJ4GF7C1Hc0jkkp2wgScyKIney8L7vWAJLpqZ75vL
+nCv8taLMAXO9QigA1NiyjZRrXr8ejpMTiWVrL6+SN6O0mBT0sf9EqsF5g0j3
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ed:3b:bb:f2:8b:20:81:de:42:41:c6:24:63:1c:
+                    4b:e5:63:b0:93:07:fd:22:64:50:7d:ef:8f:ed:65:
+                    aa:ba:f4:d9:ad:0c:68:dd:50:b0:ea:0a:5e:18:9e:
+                    df:48:88:ec:1f:fa:6b:4a:3e:db:ea:24:6e:b1:a3:
+                    bb:0b:12:de:1d:49:d3:32:78:24:f9:e8:4f:aa:85:
+                    90:21:a2:2c:8f:58:95:8c:70:80:8d:cd:99:68:03:
+                    67:0f:48:eb:96:17:63:93:2b:8f:72:77:23:5f:97:
+                    4f:86:bd:17:d2:70:5b:5c:18:f8:01:d6:11:d8:c0:
+                    dc:32:b2:f4:bf:dd:da:65:fb:86:23:c0:a4:bd:ff:
+                    c2:a4:b6:87:9e:10:98:d4:f4:09:cb:26:50:1d:56:
+                    83:72:09:c6:c1:b7:cc:52:9c:61:09:04:bb:aa:2a:
+                    63:66:a5:b1:02:60:85:bc:30:91:62:bb:6f:b0:24:
+                    33:e8:b5:9a:13:1f:3a:73:95:d5:fb:bc:a9:48:dd:
+                    14:a2:a4:62:e1:97:19:57:b1:1a:da:c1:79:93:fd:
+                    74:cb:e1:ff:0c:49:c2:78:57:8e:ef:dc:df:60:96:
+                    8e:e6:a2:97:60:b9:53:6b:17:8e:ae:f9:3d:be:31:
+                    dd:46:18:bd:af:b6:a6:02:fa:48:2f:d8:c6:f0:1f:
+                    bc:43
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                24:B9:91:41:39:F1:30:5E:F8:C5:3B:C0:51:CC:11:58:A6:13:73:B3
+            X509v3 Authority Key Identifier: 
+                keyid:CD:6F:4C:FE:AA:7A:3A:63:5D:12:79:6D:F4:4C:B0:2A:8A:7F:FB:6C
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         57:11:25:a6:73:a0:63:5a:15:05:bf:24:b7:98:df:e2:d0:01:
+         ab:4f:9e:80:3b:f7:6c:67:9b:e4:ec:19:5c:bd:bc:32:1e:40:
+         5a:8f:9f:66:f2:1a:5a:29:94:38:67:00:56:53:0f:5d:86:58:
+         a8:d8:10:f3:3e:16:ca:87:0c:95:28:64:b2:2f:db:42:31:46:
+         35:64:0a:01:f7:91:a2:ad:d5:4b:0d:78:3c:6f:72:dc:36:56:
+         d6:d5:fc:d8:c4:bd:38:15:26:ba:69:e1:75:78:e8:a0:fd:7a:
+         9d:9d:63:3b:27:35:7c:31:b7:f5:4a:1e:01:78:ee:a7:5f:b3:
+         51:3a:70:fa:2d:87:6f:d3:8f:43:df:91:c3:2a:6c:94:98:15:
+         7f:06:0a:b7:66:aa:96:f9:66:04:04:f0:b8:1c:ca:2d:f3:c7:
+         f6:fd:f2:ab:0c:33:24:fc:6f:bd:7a:b0:51:f9:a1:01:97:a0:
+         7e:9a:22:53:9c:ae:a9:38:ce:af:9d:23:42:78:eb:33:ab:46:
+         60:5e:e7:df:84:84:dd:c4:a0:4b:c3:21:e2:60:6d:29:44:97:
+         a3:94:f8:26:53:ac:f0:08:0f:c2:c1:5c:9f:23:0b:53:17:33:
+         52:1c:04:96:1a:7d:26:c2:45:48:57:01:ac:aa:b4:0f:e2:03:
+         99:bf:63:03
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Tu78osg
+gd5CQcYkYxxL5WOwkwf9ImRQfe+P7WWquvTZrQxo3VCw6gpeGJ7fSIjsH/prSj7b
+6iRusaO7CxLeHUnTMngk+ehPqoWQIaIsj1iVjHCAjc2ZaANnD0jrlhdjkyuPcncj
+X5dPhr0X0nBbXBj4AdYR2MDcMrL0v93aZfuGI8Ckvf/CpLaHnhCY1PQJyyZQHVaD
+cgnGwbfMUpxhCQS7qipjZqWxAmCFvDCRYrtvsCQz6LWaEx86c5XV+7ypSN0UoqRi
+4ZcZV7Ea2sF5k/10y+H/DEnCeFeO79zfYJaO5qKXYLlTaxeOrvk9vjHdRhi9r7am
+AvpIL9jG8B+8QwIDAQABo4HLMIHIMB0GA1UdDgQWBBQkuZFBOfEwXvjFO8BRzBFY
+phNzszAfBgNVHSMEGDAWgBTNb0z+qno6Y10SeW30TLAqin/7bDA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AFcRJaZzoGNaFQW/JLeY3+LQAatPnoA792xnm+TsGVy9vDIeQFqPn2byGloplDhn
+AFZTD12GWKjYEPM+FsqHDJUoZLIv20IxRjVkCgH3kaKt1UsNeDxvctw2VtbV/NjE
+vTgVJrpp4XV46KD9ep2dYzsnNXwxt/VKHgF47qdfs1E6cPoth2/Tj0PfkcMqbJSY
+FX8GCrdmqpb5ZgQE8Lgcyi3zx/b98qsMMyT8b716sFH5oQGXoH6aIlOcrqk4zq+d
+I0J46zOrRmBe59+EhN3EoEvDIeJgbSlEl6OU+CZTrPAID8LBXJ8jC1MXM1IcBJYa
+fSbCRUhXAayqtA/iA5m/YwM=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:e8:17:31:b6:0e:84:10:a4:9b:bf:9e:ae:9e:29:
+                    b7:6f:81:ae:d7:df:45:89:d1:29:51:0e:1e:39:7a:
+                    96:6b:7f:c0:78:df:88:cf:db:b3:ab:8d:49:0f:fb:
+                    70:55:85:4f:93:9f:12:a1:a6:55:5c:a9:ae:8d:79:
+                    4d:a6:3a:32:03:9c:bf:ad:95:c4:8b:49:1f:02:b5:
+                    23:a0:9f:da:d3:45:c6:8c:fc:ec:97:46:57:dd:77:
+                    56:c6:a2:46:78:da:a2:59:bb:22:ea:de:63:94:50:
+                    19:91:1c:10:cd:67:e0:57:10:bd:e0:de:69:67:80:
+                    6d:31:a8:43:bc:49:2c:8a:d6:4a:23:0f:a6:78:f4:
+                    74:c7:4f:37:52:3a:af:9c:03:b2:b3:6c:26:ab:62:
+                    61:12:6d:22:15:66:da:ec:d6:b8:1f:9b:14:b9:04:
+                    9c:9b:5e:b5:cb:8b:62:95:67:6a:a1:57:44:02:77:
+                    a2:81:3e:c7:20:52:a2:16:2e:ba:c2:29:a1:54:ed:
+                    33:67:f2:2a:26:a3:b6:da:08:8d:63:6c:ca:4f:c6:
+                    84:88:b9:60:08:cf:50:8e:5a:3e:75:d7:ec:d7:63:
+                    c1:fe:18:3f:4e:fb:08:de:39:45:d2:81:34:8e:89:
+                    5a:48:ce:49:bf:ca:84:cb:26:ac:c2:f7:1f:6b:3f:
+                    0d:49
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                CD:6F:4C:FE:AA:7A:3A:63:5D:12:79:6D:F4:4C:B0:2A:8A:7F:FB:6C
+            X509v3 Authority Key Identifier: 
+                keyid:CD:6F:4C:FE:AA:7A:3A:63:5D:12:79:6D:F4:4C:B0:2A:8A:7F:FB:6C
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         19:6d:6b:ec:62:f0:48:f8:4a:1a:58:9b:71:ce:df:3d:3e:19:
+         23:14:18:ab:d9:fa:12:7f:b8:ed:a7:00:2e:a8:75:6d:11:b1:
+         99:65:29:44:fe:92:5b:78:4c:1d:e5:22:92:15:16:81:95:d9:
+         0d:d4:0f:50:ea:62:80:5e:25:7b:6e:70:10:46:3c:f8:24:54:
+         d3:9e:f2:cc:30:68:63:f5:19:89:ef:00:2f:57:81:18:89:d4:
+         72:53:76:b2:9a:1c:30:19:a7:37:f9:3f:c1:be:35:ea:95:eb:
+         23:7e:66:c1:81:82:7d:64:88:64:bb:23:7a:ed:ee:14:ae:05:
+         c0:03:29:69:04:c3:4c:e8:ca:4d:78:87:e1:9f:98:8b:45:cf:
+         70:98:cb:51:e2:d3:04:03:13:e9:a1:82:d2:b8:13:f0:56:20:
+         6f:f5:59:1f:f9:96:7e:79:f7:c9:53:68:72:e5:94:bf:9f:84:
+         4c:7c:e9:01:f4:e9:1f:14:4c:83:07:c2:63:65:10:75:30:e5:
+         b8:ee:1e:74:dd:29:78:66:9b:79:46:3b:27:45:c4:5d:a9:aa:
+         a2:ed:95:28:e1:59:b9:b7:e2:85:69:e7:c4:12:36:c4:60:00:
+         fb:a3:74:69:cf:a0:ee:46:76:bd:45:c4:24:69:5b:5b:55:2e:
+         43:3e:b2:1c
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOgXMbYOhBCkm7+erp4p
+t2+BrtffRYnRKVEOHjl6lmt/wHjfiM/bs6uNSQ/7cFWFT5OfEqGmVVypro15TaY6
+MgOcv62VxItJHwK1I6Cf2tNFxoz87JdGV913VsaiRnjaolm7IureY5RQGZEcEM1n
+4FcQveDeaWeAbTGoQ7xJLIrWSiMPpnj0dMdPN1I6r5wDsrNsJqtiYRJtIhVm2uzW
+uB+bFLkEnJtetcuLYpVnaqFXRAJ3ooE+xyBSohYuusIpoVTtM2fyKiajttoIjWNs
+yk/GhIi5YAjPUI5aPnXX7Ndjwf4YP077CN45RdKBNI6JWkjOSb/KhMsmrML3H2s/
+DUkCAwEAAaOByzCByDAdBgNVHQ4EFgQUzW9M/qp6OmNdEnlt9EywKop/+2wwHwYD
+VR0jBBgwFoAUzW9M/qp6OmNdEnlt9EywKop/+2wwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZbWvsYvBI
++EoaWJtxzt89PhkjFBir2foSf7jtpwAuqHVtEbGZZSlE/pJbeEwd5SKSFRaBldkN
+1A9Q6mKAXiV7bnAQRjz4JFTTnvLMMGhj9RmJ7wAvV4EYidRyU3aymhwwGac3+T/B
+vjXqlesjfmbBgYJ9ZIhkuyN67e4UrgXAAylpBMNM6MpNeIfhn5iLRc9wmMtR4tME
+AxPpoYLSuBPwViBv9Vkf+ZZ+effJU2hy5ZS/n4RMfOkB9OkfFEyDB8JjZRB1MOW4
+7h503Sl4Zpt5RjsnRcRdqaqi7ZUo4Vm5t+KFaefEEjbEYAD7o3Rpz6DuRna9RcQk
+aVtbVS5DPrIc
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+SUCCESS
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
diff --git a/net/data/verify_certificate_chain_unittest/unconstrained-root-bad-eku.pem b/net/data/verify_certificate_chain_unittest/unconstrained-root-bad-eku.pem
new file mode 100644
index 0000000..5c47ad5
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/unconstrained-root-bad-eku.pem
@@ -0,0 +1,291 @@
+[Created by: generate-unconstrained-root-bad-eku.py]
+
+Certificate chain with 1 intermediate and a trust anchor. The trust anchor
+has an EKU that restricts it to clientAuth. Verification is expected to fail as
+the end-entity is verified for serverAuth, and the trust anchor enforces
+constraints.
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c0:64:a7:01:b2:83:6c:47:bc:2d:30:01:f9:43:
+                    8c:fc:cc:6b:7c:a4:c7:1c:78:fa:a8:8c:be:1e:9a:
+                    72:d0:34:1a:56:80:67:67:76:48:8a:9f:c5:3a:68:
+                    9e:53:c2:35:ce:69:7e:4f:d5:c4:fb:0b:91:3c:af:
+                    00:26:f4:bf:77:ca:cd:ec:87:f9:6e:05:9b:0c:93:
+                    1b:f2:6e:c8:10:32:4e:7b:51:1c:22:77:4c:b8:a3:
+                    bd:d6:dc:95:29:9b:4b:b5:d9:ce:ae:91:d8:05:c5:
+                    c5:bf:4a:9c:b7:94:db:d5:a5:e6:b1:44:e1:02:4a:
+                    1a:dc:21:e5:e6:a6:ba:54:2e:2c:3f:40:f5:fd:5c:
+                    79:dd:55:6d:9e:e2:ab:db:3c:67:b4:84:db:ba:86:
+                    fd:a0:b5:d8:8b:d0:b8:bc:8b:77:e9:32:31:51:68:
+                    ee:18:17:09:e2:f1:27:79:ca:3c:72:a8:f3:96:25:
+                    31:24:3a:05:53:d4:89:0a:48:7a:9c:2d:6d:6a:84:
+                    97:df:34:c9:22:7f:d5:05:f2:2c:91:e9:c4:7f:ab:
+                    d0:ae:76:22:64:ae:be:e2:7f:97:08:ec:86:8a:92:
+                    bf:57:f0:22:f7:91:ff:86:17:62:92:e3:80:8b:19:
+                    84:14:60:19:00:91:d6:fe:51:96:77:5b:22:0d:32:
+                    50:07
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                E8:27:22:F1:C3:94:E3:48:C4:4C:45:0D:D6:4E:1C:6E:CF:9D:1B:1B
+            X509v3 Authority Key Identifier: 
+                keyid:8F:7C:F8:3A:27:33:C2:AB:96:9E:BD:0F:68:E0:C9:58:BB:0B:7C:F2
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         2f:90:79:f0:49:4b:78:96:64:2e:4b:6b:b7:4c:09:60:c8:33:
+         24:24:f9:7e:e0:f8:2c:e7:ba:b9:41:93:97:c6:1f:ab:fa:95:
+         79:9e:23:64:6f:0a:26:d8:91:78:30:13:b3:ae:40:27:19:6e:
+         b6:49:a4:08:27:fa:0f:c8:cb:b9:9d:34:56:d6:d9:80:fc:df:
+         24:2d:e5:0f:b3:4e:22:c4:f3:da:90:4a:fb:e9:b3:37:c1:8f:
+         d1:2b:18:d4:9b:f1:0d:10:e5:98:9c:0b:4d:0a:90:92:4f:50:
+         39:87:be:52:79:39:db:c1:aa:bc:df:a8:33:66:87:cb:4f:91:
+         d8:00:21:8d:84:7b:ba:b4:d9:fa:6a:b5:06:33:cc:ef:5a:31:
+         c3:4f:c7:49:f7:59:05:b7:c4:76:58:f6:78:8c:bb:4e:95:5d:
+         a0:b7:da:e3:74:6d:4b:0f:c5:f1:c0:fd:e4:d4:68:3e:18:80:
+         2a:f1:82:6b:c8:d7:b5:70:85:4a:1e:71:ed:d2:69:7e:57:ff:
+         19:41:b5:64:aa:57:9d:08:0f:b2:74:90:d6:15:fd:9e:f0:06:
+         55:91:e9:f8:87:a1:85:9e:1f:dc:9f:33:52:c6:aa:16:a2:12:
+         b5:87:b3:9f:5c:52:f7:45:54:11:41:f0:64:1d:43:0e:9b:17:
+         00:4a:ea:21
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAZKcB
+soNsR7wtMAH5Q4z8zGt8pMccePqojL4emnLQNBpWgGdndkiKn8U6aJ5TwjXOaX5P
+1cT7C5E8rwAm9L93ys3sh/luBZsMkxvybsgQMk57URwid0y4o73W3JUpm0u12c6u
+kdgFxcW/Spy3lNvVpeaxROECShrcIeXmprpULiw/QPX9XHndVW2e4qvbPGe0hNu6
+hv2gtdiL0Li8i3fpMjFRaO4YFwni8Sd5yjxyqPOWJTEkOgVT1IkKSHqcLW1qhJff
+NMkif9UF8iyR6cR/q9CudiJkrr7if5cI7IaKkr9X8CL3kf+GF2KS44CLGYQUYBkA
+kdb+UZZ3WyINMlAHAgMBAAGjgekwgeYwHQYDVR0OBBYEFOgnIvHDlONIxExFDdZO
+HG7PnRsbMB8GA1UdIwQYMBaAFI98+DonM8Krlp69D2jgyVi7C3zyMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAL5B58ElLeJZkLktrt0wJ
+YMgzJCT5fuD4LOe6uUGTl8Yfq/qVeZ4jZG8KJtiReDATs65AJxlutkmkCCf6D8jL
+uZ00VtbZgPzfJC3lD7NOIsTz2pBK++mzN8GP0SsY1JvxDRDlmJwLTQqQkk9QOYe+
+Unk528GqvN+oM2aHy0+R2AAhjYR7urTZ+mq1BjPM71oxw0/HSfdZBbfEdlj2eIy7
+TpVdoLfa43RtSw/F8cD95NRoPhiAKvGCa8jXtXCFSh5x7dJpflf/GUG1ZKpXnQgP
+snSQ1hX9nvAGVZHp+IehhZ4f3J8zUsaqFqIStYezn1xS90VUEUHwZB1DDpsXAErq
+IQ==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a8:e7:5f:fa:d0:9d:f1:e1:e4:87:7f:62:7e:1c:
+                    89:02:66:64:9e:d5:a0:81:f3:65:68:d7:8d:02:37:
+                    99:da:e8:85:00:51:b4:69:e9:57:29:09:51:c2:78:
+                    c8:ee:bb:87:62:4a:a8:46:c3:d4:06:e5:f0:c2:33:
+                    68:13:f7:55:c5:44:42:14:1e:d7:65:a4:a1:b6:67:
+                    38:e0:c2:72:65:ee:ad:f5:94:34:93:4f:e9:d8:a5:
+                    93:98:05:34:e5:f6:0f:3b:71:84:39:71:9b:b6:10:
+                    47:37:ef:87:d2:98:29:a4:f1:18:e7:f4:3b:52:af:
+                    34:b1:39:34:9a:49:b4:7a:ed:21:2c:60:b2:01:e8:
+                    cb:b6:ad:f8:00:95:85:a9:87:91:90:05:54:0b:2e:
+                    9d:4c:79:c4:c8:6d:72:ab:23:5b:d0:2b:90:3c:5b:
+                    53:ed:da:56:39:38:37:45:43:17:3d:81:d5:49:97:
+                    23:88:83:9f:bf:86:8d:52:af:3d:86:45:f1:1e:e8:
+                    dd:8f:4f:fe:da:b5:35:cb:e0:02:ba:8e:6b:61:4a:
+                    f2:c6:5d:d7:02:95:71:23:9e:7b:99:96:cf:ac:df:
+                    20:2a:2d:fe:0c:42:72:c6:b8:c3:81:81:3e:a0:8d:
+                    62:41:17:14:f5:24:67:f1:6c:af:c6:0c:94:09:fb:
+                    56:07
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                8F:7C:F8:3A:27:33:C2:AB:96:9E:BD:0F:68:E0:C9:58:BB:0B:7C:F2
+            X509v3 Authority Key Identifier: 
+                keyid:91:69:0D:94:34:B5:BA:AF:F1:DD:99:22:88:15:2B:83:B1:37:B2:54
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         ad:25:a4:2d:f2:86:a4:d2:af:47:2c:0f:9d:8f:8b:a8:39:c1:
+         3f:32:d3:62:dc:02:69:1e:2d:ce:43:63:0f:78:19:73:da:49:
+         13:9e:32:e8:5f:61:cd:02:64:1b:b5:3c:c9:00:8d:26:ef:d2:
+         90:db:61:a9:19:59:70:e6:70:0b:d9:2b:ee:8f:00:3c:1b:50:
+         ce:07:35:3c:49:af:74:6a:36:24:1a:6c:4b:56:99:9c:ac:88:
+         79:a0:a7:56:87:35:66:c2:22:1d:79:19:a0:d8:f3:27:25:a3:
+         92:c8:49:ce:3f:06:a1:49:02:9b:09:d4:9a:1c:c7:b5:19:26:
+         31:95:6c:c4:7e:c3:fb:54:0e:62:8e:d4:13:bd:47:8e:64:be:
+         20:c5:f8:4d:c0:51:3b:2d:ca:e6:0e:b6:4e:26:38:91:aa:87:
+         5f:c9:41:2f:93:45:b8:5c:71:47:06:6f:6e:d6:ac:dd:84:b3:
+         cc:4d:c8:8d:dd:dd:41:7e:d8:24:a7:d4:48:74:4a:fe:d1:61:
+         2d:8b:9b:ad:08:40:78:cb:13:9d:67:25:9a:d8:c7:46:a2:71:
+         a0:81:b5:8f:67:f5:04:84:c3:8f:0f:6b:33:8a:2b:21:a3:1c:
+         5a:f4:5d:66:21:50:9d:a4:51:2a:2a:e9:39:c8:44:2c:f3:1f:
+         52:02:b4:0a
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqOdf+tCd
+8eHkh39ifhyJAmZkntWggfNlaNeNAjeZ2uiFAFG0aelXKQlRwnjI7ruHYkqoRsPU
+BuXwwjNoE/dVxURCFB7XZaShtmc44MJyZe6t9ZQ0k0/p2KWTmAU05fYPO3GEOXGb
+thBHN++H0pgppPEY5/Q7Uq80sTk0mkm0eu0hLGCyAejLtq34AJWFqYeRkAVUCy6d
+THnEyG1yqyNb0CuQPFtT7dpWOTg3RUMXPYHVSZcjiIOfv4aNUq89hkXxHujdj0/+
+2rU1y+ACuo5rYUryxl3XApVxI557mZbPrN8gKi3+DEJyxrjDgYE+oI1iQRcU9SRn
+8WyvxgyUCftWBwIDAQABo4HLMIHIMB0GA1UdDgQWBBSPfPg6JzPCq5aevQ9o4MlY
+uwt88jAfBgNVHSMEGDAWgBSRaQ2UNLW6r/HdmSKIFSuDsTeyVDA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AK0lpC3yhqTSr0csD52Pi6g5wT8y02LcAmkeLc5DYw94GXPaSROeMuhfYc0CZBu1
+PMkAjSbv0pDbYakZWXDmcAvZK+6PADwbUM4HNTxJr3RqNiQabEtWmZysiHmgp1aH
+NWbCIh15GaDY8yclo5LISc4/BqFJApsJ1Jocx7UZJjGVbMR+w/tUDmKO1BO9R45k
+viDF+E3AUTstyuYOtk4mOJGqh1/JQS+TRbhccUcGb27WrN2Es8xNyI3d3UF+2CSn
+1Eh0Sv7RYS2Lm60IQHjLE51nJZrYx0aicaCBtY9n9QSEw48PazOKKyGjHFr0XWYh
+UJ2kUSoq6TnIRCzzH1ICtAo=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:af:9d:d7:d1:a5:91:6e:5d:17:d4:89:85:95:b8:
+                    cf:e3:e3:fb:94:dd:cc:c0:99:59:24:ac:c0:4d:cc:
+                    4b:37:88:38:3c:a1:60:06:96:8d:1b:6b:e7:2b:b8:
+                    71:9e:54:4b:cd:c4:4d:93:b6:3b:3f:7a:a2:c6:3b:
+                    ea:9f:36:8d:e5:b0:0f:9e:27:58:7c:f8:fb:6f:e8:
+                    ae:0c:bb:69:02:60:21:d1:bd:dc:e1:33:23:8d:c5:
+                    5f:dc:ff:33:71:95:98:77:07:69:c0:71:2a:bf:62:
+                    eb:b6:e5:cc:2e:3a:98:1c:7b:a4:a7:cb:ba:e5:ab:
+                    22:32:fb:d5:03:1a:03:b7:d1:9f:d9:56:69:ae:b1:
+                    51:e7:8d:06:ca:2a:f9:25:43:af:92:a1:f7:40:60:
+                    85:5a:33:67:2a:62:ad:6e:4a:9a:02:1b:c4:e3:89:
+                    38:d3:06:eb:a3:8c:ce:a8:c8:49:5a:4e:08:b2:7e:
+                    00:16:92:60:4b:ff:77:2d:53:e7:2c:f3:2c:51:b3:
+                    16:87:67:28:43:10:d3:6c:d6:c2:96:97:a3:c8:8e:
+                    0b:ae:f1:56:13:bb:1b:ca:7f:2d:59:cc:37:fc:47:
+                    9d:f7:c9:0a:66:19:87:3d:13:66:50:0b:52:0d:13:
+                    33:6c:0b:fc:fb:88:cf:34:7b:9f:6f:6e:7e:36:ac:
+                    ec:39
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                91:69:0D:94:34:B5:BA:AF:F1:DD:99:22:88:15:2B:83:B1:37:B2:54
+            X509v3 Authority Key Identifier: 
+                keyid:91:69:0D:94:34:B5:BA:AF:F1:DD:99:22:88:15:2B:83:B1:37:B2:54
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         99:8a:58:6e:00:02:f6:8e:69:28:b4:58:15:27:8e:10:aa:46:
+         74:32:17:23:b4:06:c5:c6:71:8e:2a:f7:9d:03:97:9a:82:93:
+         a3:a0:f2:39:ee:2c:e8:ed:d6:d2:d5:bd:b0:87:c1:94:e8:66:
+         2d:01:af:9f:9b:90:88:66:3d:19:be:c8:ad:5b:53:7a:01:f1:
+         db:7e:3a:d3:aa:af:16:cc:fb:eb:b0:45:c0:2d:ab:b3:1b:65:
+         d4:94:b6:71:5d:84:3b:0a:81:07:00:14:c5:e9:b9:3e:db:6d:
+         cf:b0:5f:46:73:51:8c:5f:3f:44:45:26:28:b6:1b:53:eb:c3:
+         0b:52:58:71:21:0d:e3:7c:60:cb:86:10:64:96:ca:e4:02:b3:
+         7a:94:57:c8:1e:d5:df:b6:64:06:8b:42:3e:3c:e4:44:09:2e:
+         fe:bc:cd:60:f6:f8:e0:d5:93:d1:8d:36:8f:31:11:53:25:8e:
+         0d:e5:52:4b:b6:05:35:ee:7f:a4:a1:d5:75:26:79:de:e0:71:
+         19:13:b6:45:e7:b5:e3:66:e8:6a:35:51:b9:88:56:0b:b6:d9:
+         b7:8b:38:13:3a:ca:52:b8:02:4f:01:5c:52:aa:d0:f5:c2:1c:
+         bf:ed:60:12:30:fa:c8:ce:86:91:8a:c2:eb:30:88:53:15:e8:
+         7f:14:93:1a
+-----BEGIN TRUST_ANCHOR_UNCONSTRAINED-----
+MIIDejCCAmKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+d19GlkW5dF9SJhZW4
+z+Pj+5TdzMCZWSSswE3MSzeIODyhYAaWjRtr5yu4cZ5US83ETZO2Oz96osY76p82
+jeWwD54nWHz4+2/orgy7aQJgIdG93OEzI43FX9z/M3GVmHcHacBxKr9i67blzC46
+mBx7pKfLuuWrIjL71QMaA7fRn9lWaa6xUeeNBsoq+SVDr5Kh90BghVozZypirW5K
+mgIbxOOJONMG66OMzqjISVpOCLJ+ABaSYEv/dy1T5yzzLFGzFodnKEMQ02zWwpaX
+o8iOC67xVhO7G8p/LVnMN/xHnffJCmYZhz0TZlALUg0TM2wL/PuIzzR7n29ufjas
+7DkCAwEAAaOB4DCB3TAdBgNVHQ4EFgQUkWkNlDS1uq/x3ZkiiBUrg7E3slQwHwYD
+VR0jBBgwFoAUkWkNlDS1uq/x3ZkiiBUrg7E3slQwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqG
+SIb3DQEBCwUAA4IBAQCZilhuAAL2jmkotFgVJ44QqkZ0MhcjtAbFxnGOKvedA5ea
+gpOjoPI57izo7dbS1b2wh8GU6GYtAa+fm5CIZj0ZvsitW1N6AfHbfjrTqq8WzPvr
+sEXALauzG2XUlLZxXYQ7CoEHABTF6bk+223PsF9Gc1GMXz9ERSYothtT68MLUlhx
+IQ3jfGDLhhBklsrkArN6lFfIHtXftmQGi0I+PORECS7+vM1g9vjg1ZPRjTaPMRFT
+JY4N5VJLtgU17n+kodV1Jnne4HEZE7ZF57XjZuhqNVG5iFYLttm3izgTOspSuAJP
+AVxSqtD1why/7WASMPrIzoaRisLrMIhTFeh/FJMa
+-----END TRUST_ANCHOR_UNCONSTRAINED-----
+
+150302120000Z
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+SUCCESS
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
+
+serverAuth
+-----BEGIN KEY_PURPOSE-----
+c2VydmVyQXV0aA==
+-----END KEY_PURPOSE-----
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index 2b354cd0..40ea198 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -1347,7 +1347,7 @@
 }
 
 void SimpleEntryImpl::ChecksumOperationComplete(
-    int orig_result,
+    int original_result,
     int stream_index,
     const CompletionCallback& completion_callback,
     std::unique_ptr<int> result) {
@@ -1362,8 +1362,8 @@
   }
 
   if (*result == net::OK) {
-    *result = orig_result;
-    if (orig_result >= 0)
+    *result = original_result;
+    if (original_result >= 0)
       RecordReadResult(cache_type_, READ_RESULT_SUCCESS);
     else
       RecordReadResult(cache_type_, READ_RESULT_SYNC_READ_FAILURE);
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index 1484fad..5b58b55 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -280,8 +280,8 @@
   // Called after validating the checksums on an entry. Passes through the
   // original result if successful, propagates the error if the checksum does
   // not validate.
-  void ChecksumOperationComplete(int stream_index,
-                                 int orig_result,
+  void ChecksumOperationComplete(int original_result,
+                                 int stream_index,
                                  const CompletionCallback& completion_callback,
                                  std::unique_ptr<int> result);
 
diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc
index 18e6def..3d97a3e9 100644
--- a/net/http/http_request_headers.cc
+++ b/net/http/http_request_headers.cc
@@ -227,7 +227,7 @@
        it != header_list->end();
        ++it) {
     std::string header_line;
-    if (!(*it)->GetAsString(&header_line)) {
+    if (!it->GetAsString(&header_line)) {
       headers->Clear();
       *request_line = "";
       return false;
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 08eceab7..cb985f5c 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -1305,7 +1305,7 @@
        it != header_list->end();
        ++it) {
     std::string header_line;
-    if (!(*it)->GetAsString(&header_line))
+    if (!it->GetAsString(&header_line))
       return false;
 
     raw_headers.append(header_line);
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index 3d30297d..f927874 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -493,7 +493,7 @@
   } else {
     for (base::ListValue::const_iterator it = servers_list->begin();
          it != servers_list->end(); ++it) {
-      if (!(*it)->GetAsDictionary(&servers_dict)) {
+      if (!it->GetAsDictionary(&servers_dict)) {
         DVLOG(1) << "Malformed http_server_properties for servers dictionary.";
         detected_corrupted_prefs = true;
         continue;
@@ -651,7 +651,7 @@
   AlternativeServiceInfoVector alternative_service_info_vector;
   for (const auto& alternative_service_list_item : *alternative_service_list) {
     const base::DictionaryValue* alternative_service_dict;
-    if (!alternative_service_list_item->GetAsDictionary(
+    if (!alternative_service_list_item.GetAsDictionary(
             &alternative_service_dict))
       return false;
     AlternativeServiceInfo alternative_service_info;
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
index 66f1224d..7ab1f66 100644
--- a/net/http/http_server_properties_manager_unittest.cc
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -1398,7 +1398,7 @@
   ASSERT_TRUE(pref_dict.GetListWithoutPathExpansion("servers", &servers_list));
   base::ListValue::const_iterator it = servers_list->begin();
   const base::DictionaryValue* server_pref_dict;
-  ASSERT_TRUE((*it)->GetAsDictionary(&server_pref_dict));
+  ASSERT_TRUE(it->GetAsDictionary(&server_pref_dict));
 
   const base::DictionaryValue* example_pref_dict;
 
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index 13948e47f..cdb3109 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -107,28 +107,8 @@
 
 HttpStreamFactoryImpl::~HttpStreamFactoryImpl() {
   DCHECK(spdy_session_request_map_.empty());
-  int alt_job_count = 0;
-  int main_job_count = 0;
-  int preconnect_controller_count = 0;
-  for (const auto& it : job_controller_set_) {
-    DCHECK(it->HasPendingAltJob() || it->HasPendingMainJob());
-    // For a preconnect controller, it should have exactly the main job.
-    if (it->is_preconnect()) {
-      preconnect_controller_count++;
-      continue;
-    }
-    // For non-preconnects.
-    if (it->HasPendingAltJob())
-      alt_job_count++;
-    if (it->HasPendingMainJob())
-      main_job_count++;
-  }
-  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfPreconnect",
-                          preconnect_controller_count);
-  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectAltJob",
-                          alt_job_count);
-  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectMainJob",
-                          main_job_count);
+  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfJobControllerAtShutDown",
+                          job_controller_set_.size());
 }
 
 HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
@@ -195,6 +175,8 @@
     bool enable_ip_based_pooling,
     bool enable_alternative_services,
     const NetLogWithSource& net_log) {
+  AddJobControllerCountToHistograms();
+
   auto job_controller = base::MakeUnique<JobController>(
       this, delegate, session_, job_factory_.get(), request_info,
       /* is_preconnect = */ false, enable_ip_based_pooling,
@@ -211,6 +193,8 @@
 void HttpStreamFactoryImpl::PreconnectStreams(
     int num_streams,
     const HttpRequestInfo& request_info) {
+  AddJobControllerCountToHistograms();
+
   SSLConfig server_ssl_config;
   SSLConfig proxy_ssl_config;
   session_->GetSSLConfig(request_info, &server_ssl_config, &proxy_ssl_config);
@@ -372,6 +356,40 @@
       scheme_host_port);
 }
 
+void HttpStreamFactoryImpl::AddJobControllerCountToHistograms() const {
+  // Only log the count of JobControllers when the count is hitting one of the
+  // boundaries which is a multiple of 100: 100, 200, 300, etc.
+  if (job_controller_set_.size() < 100 || job_controller_set_.size() % 100 != 0)
+    return;
+
+  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfJobController",
+                          job_controller_set_.size());
+
+  int alt_job_count = 0;
+  int main_job_count = 0;
+  int preconnect_controller_count = 0;
+  for (const auto& job_controller : job_controller_set_) {
+    DCHECK(job_controller->HasPendingAltJob() ||
+           job_controller->HasPendingMainJob());
+    // For a preconnect controller, it should have exactly the main job.
+    if (job_controller->is_preconnect()) {
+      preconnect_controller_count++;
+      continue;
+    }
+    // For non-preconnects.
+    if (job_controller->HasPendingAltJob())
+      alt_job_count++;
+    if (job_controller->HasPendingMainJob())
+      main_job_count++;
+  }
+  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfPreconnect",
+                          preconnect_controller_count);
+  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectAltJob",
+                          alt_job_count);
+  UMA_HISTOGRAM_COUNTS_1M("Net.JobControllerSet.CountOfNonPreconnectMainJob",
+                          main_job_count);
+}
+
 void HttpStreamFactoryImpl::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
     const std::string& parent_absolute_name) const {
diff --git a/net/http/http_stream_factory_impl.h b/net/http/http_stream_factory_impl.h
index ce29d1f7..503764c4 100644
--- a/net/http/http_stream_factory_impl.h
+++ b/net/http/http_stream_factory_impl.h
@@ -171,6 +171,11 @@
   // priorities.
   bool ProxyServerSupportsPriorities(const ProxyInfo& proxy_info) const;
 
+  // Adds the count of JobControllers that are not completed to UMA histogram if
+  // the count is a multiple of 100: 100, 200, 400, etc. Break down
+  // JobControllers count based on the type of JobController.
+  void AddJobControllerCountToHistograms() const;
+
   HttpNetworkSession* const session_;
 
   // All Requests/Preconnects are assigned with a JobController to manage
diff --git a/net/test/spawned_test_server/local_test_server.cc b/net/test/spawned_test_server/local_test_server.cc
index 70ea7a94..b941b62 100644
--- a/net/test/spawned_test_server/local_test_server.cc
+++ b/net/test/spawned_test_server/local_test_server.cc
@@ -213,7 +213,7 @@
         return false;
       for (base::ListValue::const_iterator list_it = list->begin();
            list_it != list->end(); ++list_it) {
-        if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line))
+        if (!AppendArgumentFromJSONValue(key, *list_it, command_line))
           return false;
       }
     } else if (!AppendArgumentFromJSONValue(key, value, command_line)) {
diff --git a/net/tools/cert_verify_tool/cert_verify_tool.cc b/net/tools/cert_verify_tool/cert_verify_tool.cc
index 78e10bfa..5575336 100644
--- a/net/tools/cert_verify_tool/cert_verify_tool.cc
+++ b/net/tools/cert_verify_tool/cert_verify_tool.cc
@@ -126,6 +126,8 @@
     return 1;
   }
 
+  // TODO(eroman): Also use CertVerifyProcBuiltin.
+
   std::cout << "CertVerifyProc:\n";
   bool cert_verify_proc_ok = true;
   if (!time_flag.empty()) {
diff --git a/net/tools/cert_verify_tool/verify_using_path_builder.cc b/net/tools/cert_verify_tool/verify_using_path_builder.cc
index 27173064..6e2293a 100644
--- a/net/tools/cert_verify_tool/verify_using_path_builder.cc
+++ b/net/tools/cert_verify_tool/verify_using_path_builder.cc
@@ -262,7 +262,8 @@
   net::SimpleSignaturePolicy signature_policy(2048);
   net::CertPathBuilder::Result result;
   net::CertPathBuilder path_builder(target_cert, &trust_store,
-                                    &signature_policy, time, &result);
+                                    &signature_policy, time,
+                                    net::KeyPurpose::SERVER_AUTH, &result);
   path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
 #if defined(USE_NSS_CERTS)
   net::CertIssuerSourceNSS cert_issuer_source_nss;
diff --git a/remoting/client/ios/key_input_unittest.mm b/remoting/client/ios/key_input_unittest.mm
index ea6c521a..6fb0e71 100644
--- a/remoting/client/ios/key_input_unittest.mm
+++ b/remoting/client/ios/key_input_unittest.mm
@@ -60,7 +60,7 @@
 
 class KeyInputTest : public ::testing::Test {
  protected:
-  virtual void SetUp() override {
+  void SetUp() override {
     keyInput_ = [[KeyInput allocWithZone:nil] init];
     delegateTester_ = [[KeyInputDelegateTester alloc] init];
     keyInput_.delegate = delegateTester_;
diff --git a/remoting/protocol/http_ice_config_request.cc b/remoting/protocol/http_ice_config_request.cc
index 7364341..89e751d 100644
--- a/remoting/protocol/http_ice_config_request.cc
+++ b/remoting/protocol/http_ice_config_request.cc
@@ -162,13 +162,13 @@
   // Parse iceServers list and store them in |ice_config|.
   bool errors_found = false;
   for (const auto& server : *ice_servers_list) {
-    base::DictionaryValue* server_dict;
-    if (!server->GetAsDictionary(&server_dict)) {
+    const base::DictionaryValue* server_dict;
+    if (!server.GetAsDictionary(&server_dict)) {
       errors_found = true;
       continue;
     }
 
-    base::ListValue* urls_list = nullptr;
+    const base::ListValue* urls_list = nullptr;
     if (!server_dict->GetList("urls", &urls_list)) {
       errors_found = true;
       continue;
@@ -182,7 +182,7 @@
 
     for (const auto& url : *urls_list) {
       std::string url_str;
-      if (!url->GetAsString(&url_str)) {
+      if (!url.GetAsString(&url_str)) {
         errors_found = true;
         continue;
       }
diff --git a/remoting/protocol/ice_transport_unittest.cc b/remoting/protocol/ice_transport_unittest.cc
index ccb3d8d..8c5abd3 100644
--- a/remoting/protocol/ice_transport_unittest.cc
+++ b/remoting/protocol/ice_transport_unittest.cc
@@ -40,10 +40,6 @@
 const int kMessages = 100;
 const char kChannelName[] = "test_channel";
 
-ACTION_P(QuitRunLoop, run_loop) {
-  run_loop->Quit();
-}
-
 ACTION_P2(QuitRunLoopOnCounter, run_loop, counter) {
   --(*counter);
   EXPECT_GE(*counter, 0);
diff --git a/remoting/test/host_info.cc b/remoting/test/host_info.cc
index 5f5321f6..a696ffd2 100644
--- a/remoting/test/host_info.cc
+++ b/remoting/test/host_info.cc
@@ -22,7 +22,7 @@
     if (!list_value->empty()) {
       for (const auto& item : *list_value) {
         std::string token_url_pattern;
-        if (!item->GetAsString(&token_url_pattern)) {
+        if (!item.GetAsString(&token_url_pattern)) {
           return false;
         }
         token_url_patterns.push_back(token_url_pattern);
diff --git a/remoting/test/host_list_fetcher.cc b/remoting/test/host_list_fetcher.cc
index a652d14..3eb3ee0 100644
--- a/remoting/test/host_list_fetcher.cc
+++ b/remoting/test/host_list_fetcher.cc
@@ -87,10 +87,10 @@
   }
 
   // Any host_info with malformed data will not be added to the hostlist.
-  base::DictionaryValue* host_dict;
+  const base::DictionaryValue* host_dict;
   for (const auto& host_info : *hosts) {
     HostInfo host;
-    if (host_info->GetAsDictionary(&host_dict) &&
+    if (host_info.GetAsDictionary(&host_dict) &&
         host.ParseHostInfo(*host_dict)) {
       hostlist->push_back(host);
     }
diff --git a/services/catalog/entry.cc b/services/catalog/entry.cc
index 7db20262..2e2836bd 100644
--- a/services/catalog/entry.cc
+++ b/services/catalog/entry.cc
@@ -25,7 +25,7 @@
   DCHECK(string_set);
   for (const auto& value_value : list_value) {
     std::string value;
-    if (!value_value->GetAsString(&value)) {
+    if (!value_value.GetAsString(&value)) {
       LOG(ERROR) << "Entry::Deserialize: list member must be a string";
       return false;
     }
diff --git a/skia/ext/analysis_canvas_unittest.cc b/skia/ext/analysis_canvas_unittest.cc
index 9d9eb1c..18a749b 100644
--- a/skia/ext/analysis_canvas_unittest.cc
+++ b/skia/ext/analysis_canvas_unittest.cc
@@ -325,11 +325,10 @@
 }
 
 TEST(AnalysisCanvasTest, EarlyOutNotSolid) {
-  SkRTreeFactory factory;
   SkPictureRecorder recorder;
 
   // Create a picture with 3 commands, last of which is non-solid.
-  SkCanvas* record_canvas = recorder.beginRecording(256, 256, &factory);
+  SkCanvas* record_canvas = recorder.beginRecording(256, 256);
 
   std::string text = "text";
   SkPoint point = SkPoint::Make(SkIntToScalar(25), SkIntToScalar(25));
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 76ccdd7..2e42f21 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -11788,10 +11788,12 @@
           "--mus",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_mus_unittests.filter"
         ],
+        "name": "ash_mus_unittests",
+        "override_isolate_target": "ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ash_mus_unittests"
+        "test": "ash_unittests"
       },
       {
         "args": [
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 8d6eaaa3..4cb23ee 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -11954,9 +11954,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1912",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1912",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": false,
           "dimension_sets": [
@@ -12464,9 +12504,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -12987,9 +13067,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1912",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1912",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": false,
           "dimension_sets": [
@@ -13536,9 +13656,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": false,
           "dimension_sets": [
@@ -14088,9 +14248,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -14624,9 +14824,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -15111,9 +15351,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -15672,9 +15952,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": false,
           "dimension_sets": [
@@ -16239,9 +16559,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -16860,9 +17220,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -17438,9 +17838,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -17999,9 +18439,49 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=d3d9",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_d3d9_unittest",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "video_decode_accelerator_unittest",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-angle=gl",
+          "--use-test-data-path",
+          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
+        ],
+        "name": "video_decode_accelerator_gl_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 433de06..553913bc 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -1989,9 +1989,11 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -2331,9 +2333,11 @@
       },
       {
         "args": [
+          "--use-angle=d3d11",
           "--use-test-data-path",
           "--test_video_data=test-25fps.h264:320:240:250:258:::1"
         ],
+        "name": "video_decode_accelerator_d3d11_unittest",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 2d8f47bf..b8d65ca 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -4784,6 +4784,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build245-m4--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build245-m4--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -16452,6 +16511,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:22b1",
+              "id": "build141-b1",
+              "os": "Windows-10-10586",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:22b1",
+              "id": "build141-b1",
+              "os": "Windows-10-10586",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -28040,6 +28158,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:9874",
+              "id": "build205-b4",
+              "os": "Windows-10-10586",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:9874",
+              "id": "build205-b4",
+              "os": "Windows-10-10586",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 79e8380..ee533c94 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -4891,6 +4891,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build149-m1",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build149-m1",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -16499,6 +16558,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0166",
+              "id": "build103-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0166",
+              "id": "build103-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -28067,6 +28185,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "id": "build159-m1",
+              "os": "Mac-10.12",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "id": "build159-m1",
+              "os": "Mac-10.12",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -39635,6 +39812,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1626",
+              "id": "build124-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1626",
+              "id": "build124-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -51203,6 +51439,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a26",
+              "id": "build25-b1",
+              "os": "Mac-10.12",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a26",
+              "id": "build25-b1",
+              "os": "Mac-10.12",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -62771,6 +63066,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "id": "build129-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "id": "build129-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -74339,6 +74693,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0d26",
+              "id": "build5-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0d26",
+              "id": "build5-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -85907,6 +86320,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1616",
+              "id": "build118-b1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:1616",
+              "id": "build118-b1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -97495,6 +97967,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build133-m1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build133-m1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -109143,6 +109674,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "id": "build102-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "id": "build102-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -120771,6 +121361,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:041a",
+              "id": "build165-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:041a",
+              "id": "build165-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -132419,6 +133068,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "id": "build93-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "id": "build93-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -144047,6 +144755,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build186-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build186-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -155655,6 +156422,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build139-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build139-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -167283,6 +168109,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build144-m1",
+              "os": "Windows-2012ServerR2-SP0",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build144-m1",
+              "os": "Windows-2012ServerR2-SP0",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
@@ -178851,6 +179736,65 @@
       },
       {
         "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:161e",
+              "id": "build31-b1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "power.idle_platform.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:161e",
+              "id": "build31-b1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 7200,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "power.steady_state",
           "-v",
           "--upload-results",
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index c07ff7e..4ad06da 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -148,15 +148,6 @@
     "label": "//ash:ash_unittests",
     "type": "windowed_test_launcher",
   },
-  "ash_mus_unittests": {
-    "label": "//ash:ash_unittests",
-    "label_type": "group",
-    "type": "windowed_test_launcher",
-    "args": [
-      "--mus",
-    ],
-    "executable": "ash_unittests",
-  },
   "audio_unittests": {
     "label": "//media:audio_unittests",
     "type": "raw",
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 2227562..76e9613 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1752,6 +1752,7 @@
 crbug.com/705125 http/tests/streams/readable-streams/cancel.html [ Failure ]
 crbug.com/705125 http/tests/streams/readable-streams/templated.html [ Failure ]
 crbug.com/705125 http/tests/w3c/webperf/submission/Intel/user-timing/test_user_timing_clearMarks.html [ Failure ]
+crbug.com/705125 virtual/mojo-loading/http/tests/w3c/webperf/submission/Intel/user-timing/test_user_timing_clearMarks.html [ Failure ]
 crbug.com/705125 virtual/mojo-loading/http/tests/security/cors-rfc1918/addressspace-document-appcache.html [ Failure ]
 crbug.com/705125 virtual/mojo-loading/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.html [ Failure ]
 crbug.com/705125 virtual/mojo-loading/http/tests/streams/readable-streams/cancel.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-basic-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-basic-expected.txt
index 26d7327f..4a144ed4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-basic-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-basic-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS Create headers from no parameter 
-FAIL Create headers from undefined parameter Failed to construct 'Headers': No matching constructor signature.
+PASS Create headers from undefined parameter 
 PASS Create headers from empty object 
 PASS Create headers with null should throw 
 PASS Create headers with 1 should throw 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-record-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-record-expected.txt
index 26d24fc..f702de2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-record-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-record-expected.txt
@@ -1,14 +1,14 @@
 This is a testharness.js-based test.
 PASS Passing nothing to Headers constructor 
-FAIL Passing undefined to Headers constructor Failed to construct 'Headers': No matching constructor signature.
+PASS Passing undefined to Headers constructor 
 PASS Passing null to Headers constructor 
-FAIL Basic operation with one property assert_equals: expected 4 but got 5
-FAIL Basic operation with one property and a proto assert_equals: expected 4 but got 7
-FAIL Correct operation ordering with two properties assert_equals: expected 6 but got 8
-FAIL Correct operation ordering with two properties one of which has an invalid name assert_equals: expected 5 but got 8
-FAIL Correct operation ordering with two properties one of which has an invalid value assert_equals: expected 4 but got 6
-FAIL Correct operation ordering with non-enumerable properties assert_equals: expected 6 but got 7
-FAIL Correct operation ordering with undefined descriptors assert_equals: expected 6 but got 7
-FAIL Correct operation ordering with repeated keys assert_equals: expected 9 but got 10
+FAIL Basic operation with one property assert_equals: expected 4 but got 3
+FAIL Basic operation with one property and a proto assert_equals: expected 4 but got 3
+FAIL Correct operation ordering with two properties assert_equals: expected 6 but got 5
+FAIL Correct operation ordering with two properties one of which has an invalid name assert_equals: expected 5 but got 4
+FAIL Correct operation ordering with two properties one of which has an invalid value assert_equals: expected 4 but got 3
+FAIL Correct operation ordering with non-enumerable properties assert_equals: expected 6 but got 5
+FAIL Correct operation ordering with undefined descriptors assert_equals: expected 6 but got 5
+FAIL Correct operation ordering with repeated keys assert_equals: expected 9 but got 8
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args-expected.txt
index f7af080c..7a58fc7 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args-expected.txt
@@ -22,8 +22,8 @@
 PASS view neutered
 PASS Posting message ('[object ArrayBuffer]', ): threw exception DataCloneError: Failed to execute 'postMessage' on 'Window': An ArrayBuffer is neutered and could not be cloned.
 PASS Posting message ('data', [object ArrayBuffer]): threw exception DataCloneError: Failed to execute 'postMessage' on 'Window': ArrayBuffer at index 0 is already neutered.
-PASS Posting message ('', ): threw exception DataCloneError: Failed to execute 'postMessage' on 'Window': An ArrayBuffer is neutered and could not be cloned.
-PASS Posting message ('data', ): threw exception TypeError: Failed to execute 'postMessage' on 'Window': Value at index 0 does not have a transferable type.
+PASS Posting message ('[detached TypedArray]', ): threw exception DataCloneError: Failed to execute 'postMessage' on 'Window': An ArrayBuffer is neutered and could not be cloned.
+PASS Posting message ('data', [detached TypedArray]): threw exception TypeError: Failed to execute 'postMessage' on 'Window': Value at index 0 does not have a transferable type.
 PASS Posting message ('data', [object Object]): threw exception TypeError: Failed to execute 'postMessage' on 'Window': Value at index 0 is an untransferable 'undefined' value.
 PASS Posting message ('data', 1,,2): threw exception TypeError: Failed to execute 'postMessage' on 'Window': Value at index 0 does not have a transferable type.
 PASS Posting message ('data', ,function () { [native code] }): threw exception TypeError: Failed to execute 'postMessage' on 'Window': Value at index 0 is an untransferable 'null' value.
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args.html b/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args.html
index 7f2eeb8..ee07a6e0 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/window-postmessage-args.html
@@ -25,6 +25,18 @@
 
 window.addEventListener('message', onmessage, false);
 
+function convertDetached(obj) {
+    if (obj instanceof Int8Array && obj.byteLength === 0)
+        return "[detached TypedArray]";
+    return obj;
+}
+
+function convertArrayDetached(obj) {
+    if (obj instanceof Array)
+        return obj.map(convertDetached)
+    return obj;
+}
+
 function tryPostMessageFunction(postMessageFunction, first, second, third, shouldFail) {
     var pass, reason;
     try {
@@ -36,9 +48,9 @@
         reason = ": threw exception " + e;
     }
     if (pass)
-        testPassed("Posting message ('" + first + "', " + third + ")" + reason);
+        testPassed("Posting message ('" + convertDetached(first) + "', " + convertArrayDetached(third) + ")" + reason);
     else
-        testFailed("Posting message ('" + first + "', " + third + ")" + reason);
+        testFailed("Posting message ('" + convertDetached(first) + "', " + convertArrayDetached(third) + ")" + reason);
 }
 
 function tryPostMessage(first, second, third, shouldFail) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/request.js b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/request.js
index 0c1688d3..b6aba93 100644
--- a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/request.js
+++ b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/request.js
@@ -129,6 +129,32 @@
                   'Request.headers should match');
     assert_equals(request3.headers.getAll('X-Fetch-Bar')[0], 'bar',
                   'Request.headers should match');
+    var request4 = new Request(URL,
+                               {headers: {'X-Fetch-Foo': 'foo1',
+                                          'X-Fetch-Foo': 'foo2',
+                                          'X-Fetch-Bar': 'bar'}});
+    assert_equals(request4.headers.getAll('X-Fetch-Foo')[0], 'foo2',
+                  'Request.headers should match');
+    assert_equals(request4.headers.getAll('X-Fetch-Bar')[0], 'bar',
+                  'Request.headers should match');
+    // https://github.com/whatwg/fetch/issues/479
+    var request5 = new Request(request, {headers: undefined});
+    assert_equals(request5.headers.getAll('X-Fetch-Foo')[0], 'foo1',
+                  'Request.headers should match');
+    assert_equals(request5.headers.getAll('X-Fetch-Foo')[1], 'foo2',
+                  'Request.headers should match');
+    assert_equals(request5.headers.getAll('X-Fetch-Bar')[0], 'bar',
+                  'Request.headers should match');
+    var request6 = new Request(request, {});
+    assert_equals(request6.headers.getAll('X-Fetch-Foo')[0], 'foo1',
+                  'Request.headers should match');
+    assert_equals(request6.headers.getAll('X-Fetch-Foo')[1], 'foo2',
+                  'Request.headers should match');
+    assert_equals(request6.headers.getAll('X-Fetch-Bar')[0], 'bar',
+                  'Request.headers should match');
+    assert_throws(new TypeError(),
+                  () => { new Request(request, {headers: null}) },
+                  'null cannot be converted to a HeaderInit');
   }, 'Request header test');
 
 test(function() {
@@ -695,7 +721,7 @@
 
 test(function() {
     // https://fetch.spec.whatwg.org/#dom-request
-    // Step 20:
+    // Step 32:
     // Fill r's Headers object with headers. Rethrow any exceptions.
     INVALID_HEADER_NAMES.forEach(function(name) {
         assert_throws(
diff --git a/third_party/WebKit/LayoutTests/media/avtrack/track-switching.html b/third_party/WebKit/LayoutTests/media/avtrack/track-switching.html
new file mode 100644
index 0000000..88f6f1a8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/avtrack/track-switching.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Media track switching during playback</title>
+        <script src="../../resources/testharness.js"></script>
+        <script src="../../resources/testharnessreport.js"></script>
+        <script src="../media-file.js"></script>
+    </head>
+    <body>
+        <script>
+            async_test(function(t)
+            {
+                var video = document.createElement("video");
+                video.src = "../content/multitrack-3video-2audio.webm";
+
+                video.onloadedmetadata = t.step_func(function()
+                {
+                    assert_equals(video.videoTracks.length, 3, "videoTracks.length");
+                    assert_equals(video.audioTracks.length, 2, "audioTracks.length");
+
+                    assert_true(video.videoTracks[0].selected, "videoTrack[0].selected");
+                    assert_false(video.videoTracks[1].selected, "videoTrack[1].selected");
+                    assert_false(video.videoTracks[2].selected, "videoTrack[2].selected");
+                    assert_true(video.audioTracks[0].enabled, "audioTracks[0].enabled");
+                    assert_false(video.audioTracks[1].enabled, "audioTracks[1].enabled");
+
+                    var videoWatcher = new EventWatcher(t, video, ["playing"]);
+                    var audioTracksWatcher = new EventWatcher(t, video.audioTracks, ["change"]);
+                    var videoTracksWatcher = new EventWatcher(t, video.videoTracks, ["change"]);
+
+                    videoWatcher.wait_for("playing").then(t.step_func(function() {
+                        video.videoTracks[1].selected = true;
+                        return videoTracksWatcher.wait_for("change");
+                    })).then(t.step_func(function() {
+                        assert_false(video.videoTracks[0].selected, "videoTrack[0].selected");
+                        assert_true(video.videoTracks[1].selected, "videoTrack[1].selected");
+                        video.audioTracks[0].enabled = false;
+                        return audioTracksWatcher.wait_for("change");
+                    })).then(t.step_func(function() {
+                        assert_false(video.audioTracks[0].enabled, "audioTracks[0].enabled");
+                        assert_false(video.audioTracks[1].enabled, "audioTracks[1].enabled");
+                        video.audioTracks[1].enabled = true;
+                        return audioTracksWatcher.wait_for("change");
+                    })).then(t.step_func(function() {
+                        assert_false(video.audioTracks[0].enabled, "audioTracks[0].enabled");
+                        assert_true(video.audioTracks[1].enabled, "audioTracks[1].enabled");
+                        setTimeout(t.step_func_done(), 500);
+                    }));
+                    video.play();
+                });
+            }, "VideoTrackList track change");
+        </script>
+    </body>
+</html>
+
diff --git a/third_party/WebKit/LayoutTests/media/content/multitrack-3video-2audio.webm b/third_party/WebKit/LayoutTests/media/content/multitrack-3video-2audio.webm
new file mode 100644
index 0000000..473db88
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/content/multitrack-3video-2audio.webm
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners.html b/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners.html
index e5cda4e7..6ff207c 100644
--- a/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners.html
+++ b/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners.html
@@ -11,7 +11,7 @@
 shouldBe('typeof window.internals.observeGC', '"function"',
 'this test requires window.internals');
 
-var callback = function(e) {
+let callback = function(e) {
 	testFailed("Should not get here.");
 };
 
diff --git a/third_party/WebKit/LayoutTests/storage/indexeddb/cursor-request-cycle.html b/third_party/WebKit/LayoutTests/storage/indexeddb/cursor-request-cycle.html
index 4f65843..1d09df9 100644
--- a/third_party/WebKit/LayoutTests/storage/indexeddb/cursor-request-cycle.html
+++ b/third_party/WebKit/LayoutTests/storage/indexeddb/cursor-request-cycle.html
@@ -73,7 +73,10 @@
             shouldBeEqualToString("cursor.key", "key2");
             shouldBeEqualToString("cursor.value", "value2");
 
-            cursorObservation = internals.observeGC(cursor);
+            // Access objects in an inner function to avoid references to
+            // objects remaining live on this function's stack frame
+            // (http://crbug.com/595672/).
+            (() => { cursorObservation = internals.observeGC(cursor); })();
             evalAndLog("cursor = null");
             evalAndLog("gc()");
             shouldBeTrue("cursorRequestObservation.wasCollected");
diff --git a/third_party/WebKit/LayoutTests/storage/indexeddb/key-cursor-request-cycle.html b/third_party/WebKit/LayoutTests/storage/indexeddb/key-cursor-request-cycle.html
index 4b24ace25..a0e99fa3 100644
--- a/third_party/WebKit/LayoutTests/storage/indexeddb/key-cursor-request-cycle.html
+++ b/third_party/WebKit/LayoutTests/storage/indexeddb/key-cursor-request-cycle.html
@@ -70,7 +70,10 @@
             preamble(evt);
             shouldBeEqualToString("cursor.key", "key2");
 
-            cursorObservation = internals.observeGC(cursor);
+            // Access objects in an inner function to avoid references to
+            // objects remaining live on this function's stack frame
+            // (http://crbug.com/595672/).
+            (() => { cursorObservation = internals.observeGC(cursor); })();
             evalAndLog("cursor = null");
             evalAndLog("gc()");
             shouldBeTrue("cursorRequestObservation.wasCollected");
diff --git a/third_party/WebKit/LayoutTests/svg/animations/animate-values-whitespace.html b/third_party/WebKit/LayoutTests/svg/animations/animate-values-whitespace.html
new file mode 100644
index 0000000..8be8817
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/animations/animate-values-whitespace.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>SMIL 'values' whitespace</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<svg></svg>
+<script>
+let subjects = [
+  { char: '\u0009', stripped: true,  name: 'Character Tabulation' },
+  { char: '\u000A', stripped: true,  name: 'Line Feed' },
+  { char: '\u000C', stripped: true,  name: 'Form Feed' },
+  { char: '\u000D', stripped: true,  name: 'Carriage Return' },
+  { char: '\u0020', stripped: true,  name: 'Space' },
+  { char: '\u00A0', stripped: false, name: 'No-Break Space' },
+  { char: '\u1680', stripped: false, name: 'Ogham Space Mark' },
+  { char: '\u2000', stripped: false, name: 'EN Quad' },
+  { char: '\u2001', stripped: false, name: 'EM Quad' },
+  { char: '\u2002', stripped: false, name: 'EN Space' },
+  { char: '\u2003', stripped: false, name: 'EM Space' },
+  { char: '\u2004', stripped: false, name: 'Three-Per-Em Space' },
+  { char: '\u2005', stripped: false, name: 'Four-Per-Em Space' },
+  { char: '\u2006', stripped: false, name: 'Six-Per-Em Space' },
+  { char: '\u2007', stripped: false, name: 'Figure Space' },
+  { char: '\u2008', stripped: false, name: 'Punctuation Space' },
+  { char: '\u2009', stripped: false, name: 'Thin Space' },
+  { char: '\u200A', stripped: false, name: 'Hair Space' },
+  { char: '\u202F', stripped: false, name: 'Narrow No-Break Space' },
+  { char: '\u205F', stripped: false, name: 'Medium Mathematical Space' },
+  { char: '\u3000', stripped: false, name: 'Ideographic Space' },
+];
+
+function makeTestSubject(subjectText) {
+  const svgNs = 'http://www.w3.org/2000/svg';
+  let aLink = document.createElementNS(svgNs, 'a');
+  let rect = aLink.appendChild(document.createElementNS(svgNs, 'rect'));
+  rect.setAttribute('width', 100);
+  rect.setAttribute('height', 100);
+  rect.setAttribute('fill', 'blue');
+  let animate = aLink.appendChild(document.createElementNS(svgNs, 'animate'));
+  animate.setAttribute('attributeName', 'href');
+  animate.setAttribute('values', subjectText);
+  return aLink;
+}
+
+let svgRoot = document.querySelector('svg');
+for (let subject of subjects) {
+  async_test(function(t) {
+    let payload = 'javascript:alert("' + subject.name + '")';
+    let fragment = makeTestSubject(subject.char + payload);
+    svgRoot.appendChild(fragment);
+    let expected = (subject.stripped ? '' : subject.char) + payload;
+    let animate = fragment.lastChild;
+    animate.onbegin = t.step_func_done(function() {
+      var target = animate.parentNode;
+      assert_equals(target.href.animVal, expected);
+    });
+  }, subject.name + ' (U+' +  subject.char.codePointAt(0).toString(16) +  ') is ' +
+     (subject.stripped ? '' : 'not ') + 'stripped.');
+};
+</script>
diff --git a/third_party/WebKit/Source/bindings/bindings.gni b/third_party/WebKit/Source/bindings/bindings.gni
index 5a23a87..9fc25956 100644
--- a/third_party/WebKit/Source/bindings/bindings.gni
+++ b/third_party/WebKit/Source/bindings/bindings.gni
@@ -213,6 +213,8 @@
                     "core/v8/WorkerOrWorkletScriptController.cpp",
                     "core/v8/WorkerOrWorkletScriptController.h",
                     "core/v8/WorkerV8Settings.h",
+                    "core/v8/WrapperCreationSecurityCheck.cpp",
+                    "core/v8/WrapperCreationSecurityCheck.h",
                     "core/v8/WrapperTypeInfo.cpp",
                     "core/v8/WrapperTypeInfo.h",
                     "core/v8/serialization/V8ScriptValueDeserializer.cpp",
diff --git a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
index 662b7e1..83fb7b5 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
@@ -32,6 +32,8 @@
 
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/V8Binding.h"
+#include "bindings/core/v8/V8Location.h"
+#include "bindings/core/v8/WrapperCreationSecurityCheck.h"
 #include "core/dom/Document.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
@@ -51,7 +53,7 @@
   SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
                  target_window == target_window->GetFrame()->DomWindow());
 
-  // It's important to check that targetWindow is a LocalDOMWindow: it's
+  // It's important to check that target_window is a LocalDOMWindow: it's
   // possible for a remote frame and local frame to have the same security
   // origin, depending on the model being used to allocate Frames between
   // processes. See https://crbug.com/601629.
@@ -264,6 +266,65 @@
   return true;
 }
 
+bool BindingSecurity::ShouldAllowAccessToCreationContext(
+    v8::Local<v8::Context> creation_context,
+    const WrapperTypeInfo* type) {
+  // According to
+  // https://html.spec.whatwg.org/multipage/browsers.html#security-location,
+  // cross-origin script access to a few properties of Location is allowed.
+  // Location already implements the necessary security checks.
+  if (type->Equals(&V8Location::wrapperTypeInfo))
+    return true;
+
+  v8::Isolate* isolate = creation_context->GetIsolate();
+  LocalFrame* frame = ToLocalFrameIfNotDetached(creation_context);
+  ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
+                                 type->interface_name);
+  if (!frame) {
+    // Sandbox detached frames - they can't create cross origin objects.
+    LocalDOMWindow* calling_window = CurrentDOMWindow(isolate);
+    LocalDOMWindow* target_window = ToLocalDOMWindow(creation_context);
+
+    return ShouldAllowAccessToDetachedWindow(calling_window, target_window,
+                                             exception_state);
+  }
+  const DOMWrapperWorld& current_world =
+      DOMWrapperWorld::World(isolate->GetCurrentContext());
+  CHECK_EQ(current_world.GetWorldId(),
+           DOMWrapperWorld::World(creation_context).GetWorldId());
+
+  return !current_world.IsMainWorld() ||
+         ShouldAllowAccessToFrame(CurrentDOMWindow(isolate), frame,
+                                  exception_state);
+}
+
+void BindingSecurity::RethrowCrossContextException(
+    v8::Local<v8::Context> creation_context,
+    const WrapperTypeInfo* type,
+    v8::Local<v8::Value> cross_context_exception) {
+  DCHECK(!cross_context_exception.IsEmpty());
+  v8::Isolate* isolate = creation_context->GetIsolate();
+  ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
+                                 type->interface_name);
+  if (type->Equals(&V8Location::wrapperTypeInfo)) {
+    // Convert cross-context exception to security error
+    LocalDOMWindow* calling_window = CurrentDOMWindow(isolate);
+    LocalDOMWindow* target_window = ToLocalDOMWindow(creation_context);
+    exception_state.ThrowSecurityError(
+        target_window->SanitizedCrossDomainAccessErrorMessage(calling_window),
+        target_window->CrossDomainAccessErrorMessage(calling_window));
+    return;
+  }
+  exception_state.RethrowV8Exception(cross_context_exception);
+}
+
+void BindingSecurity::InitWrapperCreationSecurityCheck() {
+  WrapperCreationSecurityCheck::SetSecurityCheckFunction(
+      &ShouldAllowAccessToCreationContext);
+  WrapperCreationSecurityCheck::SetRethrowExceptionFunction(
+      &RethrowCrossContextException);
+}
+
 void BindingSecurity::FailedAccessCheckFor(v8::Isolate* isolate,
                                            const Frame* target) {
   // TODO(dcheng): See if this null check can be removed or hoisted to a
diff --git a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.h b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.h
index c04db99..92d907c2 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.h
+++ b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.h
@@ -44,6 +44,7 @@
 class LocalDOMWindow;
 class Location;
 class Node;
+struct WrapperTypeInfo;
 
 class CORE_EXPORT BindingSecurity {
   STATIC_ONLY(BindingSecurity);
@@ -118,6 +119,23 @@
 
   static void FailedAccessCheckFor(v8::Isolate*, const Frame* target);
 
+  // The following two functions were written to be called by
+  // V8WrapperInstantiationScope before entering and after exiting an object's
+  // creation context during wrapper creation.
+
+  // Returns true if the current context has access to creationContext, and
+  // throws a SecurityError if it doesn't have access.
+  static bool ShouldAllowAccessToCreationContext(
+      v8::Local<v8::Context> creation_context,
+      const WrapperTypeInfo*);
+
+  static void RethrowCrossContextException(
+      v8::Local<v8::Context> creation_context,
+      const WrapperTypeInfo*,
+      v8::Local<v8::Value> cross_context_exception);
+
+  static void InitWrapperCreationSecurityCheck();
+
  private:
   // Returns true if |accessingWindow| is allowed named access to |targetWindow|
   // because they're the same origin.  Note that named access should be allowed
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
index 991ea01..7db3171 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
@@ -157,8 +157,8 @@
   } else {
     DVLOG(1) << "ScheduledAction::execute " << this
              << ": executing from source";
-    frame->Script().ExecuteScriptAndReturnValue(script_state_->GetContext(),
-                                                ScriptSourceCode(code_));
+    frame->GetScriptController().ExecuteScriptAndReturnValue(
+        script_state_->GetContext(), ScriptSourceCode(code_));
   }
 
   // The frame might be invalid at this point because JavaScript could have
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.cpp
index de04e24b..8a6427a 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.cpp
@@ -62,7 +62,7 @@
 
   v8::Isolate* isolate = ToIsolate(&node->GetDocument());
   if (LocalFrame* frame = node->GetDocument().GetFrame()) {
-    ScriptController& script_controller = frame->Script();
+    ScriptController& script_controller = frame->GetScriptController();
     if (!node->GetDocument().CanExecuteScripts(kAboutToExecuteScript))
       return nullptr;
     position = script_controller.EventHandlerPosition();
@@ -88,7 +88,7 @@
   if (!frame->GetDocument()->CanExecuteScripts(kAboutToExecuteScript))
     return nullptr;
 
-  TextPosition position = frame->Script().EventHandlerPosition();
+  TextPosition position = frame->GetScriptController().EventHandlerPosition();
   String source_url = frame->GetDocument()->Url().GetString();
 
   return V8LazyEventListener::Create(name.LocalName(), event_parameter_name,
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
index 12bdfe52..640c955 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
@@ -19,7 +19,7 @@
 }
 
 ScriptSourceCode::ScriptSourceCode(ScriptResource* resource)
-    : source_(resource->Script()),
+    : source_(resource->SourceText()),
       resource_(resource),
       start_position_(TextPosition::MinimumPosition()) {
   TreatNullSourceAsEmpty();
@@ -27,7 +27,7 @@
 
 ScriptSourceCode::ScriptSourceCode(ScriptStreamer* streamer,
                                    ScriptResource* resource)
-    : source_(resource->Script()),
+    : source_(resource->SourceText()),
       resource_(resource),
       streamer_(streamer),
       start_position_(TextPosition::MinimumPosition()) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.h b/third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.h
index ef5dce27..0c71436 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.h
@@ -106,6 +106,9 @@
   void ClearListenerObject();
 
   bool BelongsToTheCurrentWorld(ExecutionContext*) const final;
+
+  bool IsAttribute() const final { return is_attribute_; }
+
   v8::Isolate* GetIsolate() const { return isolate_; }
   DOMWrapperWorld& World() const { return *world_; }
 
@@ -128,9 +131,6 @@
   v8::Local<v8::Object> GetReceiverObject(ScriptState*, Event*);
 
  private:
-  // Implementation of EventListener function.
-  bool VirtualisAttribute() const override { return is_attribute_; }
-
   // This could return an empty handle and callers need to check return value.
   // We don't use v8::MaybeLocal because it can fail without exception.
   virtual v8::Local<v8::Value>
@@ -143,7 +143,7 @@
 
   TraceWrapperV8Reference<v8::Object> listener_;
 
-  // Indicates if this is an HTML type listener.
+  // true if the listener is created through a DOM attribute.
   bool is_attribute_;
 
   RefPtr<DOMWrapperWorld> world_;
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp b/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp
index 249080d6..efbe7870 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp
@@ -31,14 +31,9 @@
 #include "bindings/core/v8/V8DOMWrapper.h"
 
 #include "bindings/core/v8/V8Binding.h"
-#include "bindings/core/v8/V8Location.h"
 #include "bindings/core/v8/V8ObjectConstructor.h"
 #include "bindings/core/v8/V8PerContextData.h"
 #include "bindings/core/v8/V8PerIsolateData.h"
-#include "bindings/core/v8/V8ScriptRunner.h"
-#include "bindings/core/v8/V8Window.h"
-#include "core/dom/Document.h"
-#include "core/frame/LocalDOMWindow.h"
 
 namespace blink {
 
@@ -46,14 +41,10 @@
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
     const WrapperTypeInfo* type) {
-  ASSERT(!type->Equals(&V8Window::wrapperTypeInfo));
-  // According to
-  // https://html.spec.whatwg.org/multipage/browsers.html#security-location,
-  // cross-origin script access to a few properties of Location is allowed.
-  // Location already implements the necessary security checks.
-  bool with_security_check = !type->Equals(&V8Location::wrapperTypeInfo);
-  V8WrapperInstantiationScope scope(creation_context, isolate,
-                                    with_security_check);
+  V8WrapperInstantiationScope scope(creation_context, isolate, type);
+  if (scope.AccessCheckFailed()) {
+    return v8::Local<v8::Object>();
+  }
 
   V8PerContextData* per_context_data =
       V8PerContextData::From(scope.GetContext());
@@ -106,54 +97,4 @@
          untrusted_wrapper_type_info->gin_embedder == gin::kEmbedderBlink;
 }
 
-void V8WrapperInstantiationScope::SecurityCheck(
-    v8::Isolate* isolate,
-    v8::Local<v8::Context> context_for_wrapper) {
-  if (context_.IsEmpty())
-    return;
-  // If the context is different, we need to make sure that the current
-  // context has access to the creation context.
-  LocalFrame* frame = ToLocalFrameIfNotDetached(context_for_wrapper);
-  if (!frame) {
-    // Sandbox detached frames - they can't create cross origin objects.
-    LocalDOMWindow* calling_window = CurrentDOMWindow(isolate);
-    LocalDOMWindow* target_window = ToLocalDOMWindow(context_for_wrapper);
-    // TODO(jochen): Currently, Location is the only object for which we can
-    // reach this code path. Should be generalized.
-    ExceptionState exception_state(
-        isolate, ExceptionState::kConstructionContext, "Location");
-    if (BindingSecurity::ShouldAllowAccessToDetachedWindow(
-            calling_window, target_window, exception_state))
-      return;
-
-    CHECK_EQ(kSecurityError, exception_state.Code());
-    return;
-  }
-  const DOMWrapperWorld& current_world = DOMWrapperWorld::World(context_);
-  RELEASE_ASSERT(current_world.GetWorldId() ==
-                 DOMWrapperWorld::World(context_for_wrapper).GetWorldId());
-  // TODO(jochen): Add the interface name here once this is generalized.
-  ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
-                                 nullptr);
-  if (current_world.IsMainWorld() &&
-      !BindingSecurity::ShouldAllowAccessToFrame(CurrentDOMWindow(isolate),
-                                                 frame, exception_state)) {
-    CHECK_EQ(kSecurityError, exception_state.Code());
-    return;
-  }
-}
-
-void V8WrapperInstantiationScope::ConvertException() {
-  v8::Isolate* isolate = context_->GetIsolate();
-  // TODO(jochen): Currently, Location is the only object for which we can reach
-  // this code path. Should be generalized.
-  ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
-                                 "Location");
-  LocalDOMWindow* calling_window = CurrentDOMWindow(isolate);
-  LocalDOMWindow* target_window = ToLocalDOMWindow(context_);
-  exception_state.ThrowSecurityError(
-      target_window->SanitizedCrossDomainAccessErrorMessage(calling_window),
-      target_window->CrossDomainAccessErrorMessage(calling_window));
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h b/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h
index a9944a6..94f6ba3e 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h
@@ -31,10 +31,10 @@
 #ifndef V8DOMWrapper_h
 #define V8DOMWrapper_h
 
-#include "bindings/core/v8/BindingSecurity.h"
 #include "bindings/core/v8/DOMDataStore.h"
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "bindings/core/v8/V8Binding.h"
+#include "bindings/core/v8/WrapperCreationSecurityCheck.h"
 #include "core/CoreExport.h"
 #include "platform/wtf/Compiler.h"
 #include "platform/wtf/text/AtomicString.h"
@@ -127,11 +127,12 @@
  public:
   V8WrapperInstantiationScope(v8::Local<v8::Object> creation_context,
                               v8::Isolate* isolate,
-                              bool with_security_check)
+                              const WrapperTypeInfo* type)
       : did_enter_context_(false),
         context_(isolate->GetCurrentContext()),
         try_catch_(isolate),
-        convert_exceptions_(false) {
+        type_(type),
+        access_check_failed_(false) {
     // creationContext should not be empty. Because if we have an
     // empty creationContext, we will end up creating
     // a new object in the context currently entered. This is wrong.
@@ -143,12 +144,16 @@
     // context is different from the context that we are about to enter.
     if (context_for_wrapper == context_)
       return;
-    if (with_security_check) {
-      SecurityCheck(isolate, context_for_wrapper);
-    } else {
-      convert_exceptions_ = true;
+
+    context_ = context_for_wrapper;
+
+    if (!WrapperCreationSecurityCheck::VerifyContextAccess(context_, type_)) {
+      DCHECK(try_catch_.HasCaught());
+      try_catch_.ReThrow();
+      access_check_failed_ = true;
+      return;
     }
-    context_ = v8::Local<v8::Context>::New(isolate, context_for_wrapper);
+
     did_enter_context_ = true;
     context_->Enter();
   }
@@ -159,26 +164,30 @@
       return;
     }
     context_->Exit();
-    // Rethrow any cross-context exceptions as security error.
-    if (try_catch_.HasCaught()) {
-      if (convert_exceptions_) {
-        try_catch_.Reset();
-        ConvertException();
-      }
-      try_catch_.ReThrow();
-    }
+
+    if (!try_catch_.HasCaught())
+      return;
+
+    // Any exception caught here is a cross context exception and it may not be
+    // safe to directly rethrow the exception in the current context (without
+    // converting it). rethrowCrossContextException converts the exception in
+    // such a scenario.
+    v8::Local<v8::Value> caught_exception = try_catch_.Exception();
+    try_catch_.Reset();
+    WrapperCreationSecurityCheck::RethrowCrossContextException(
+        context_, type_, caught_exception);
+    try_catch_.ReThrow();
   }
 
   v8::Local<v8::Context> GetContext() const { return context_; }
+  bool AccessCheckFailed() const { return access_check_failed_; }
 
  private:
-  void SecurityCheck(v8::Isolate*, v8::Local<v8::Context> context_for_wrapper);
-  void ConvertException();
-
   bool did_enter_context_;
   v8::Local<v8::Context> context_;
   v8::TryCatch try_catch_;
-  bool convert_exceptions_;
+  const WrapperTypeInfo* type_;
+  bool access_check_failed_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
index db5a382..ecf7733 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -27,6 +27,7 @@
 
 #include <memory>
 
+#include "bindings/core/v8/BindingSecurity.h"
 #include "bindings/core/v8/DOMWrapperWorld.h"
 #include "bindings/core/v8/RejectedPromises.h"
 #include "bindings/core/v8/RetainedDOMInfo.h"
@@ -501,6 +502,8 @@
 
   V8PerIsolateData::From(isolate)->SetThreadDebugger(
       WTF::MakeUnique<MainThreadDebugger>(isolate));
+
+  BindingSecurity::InitWrapperCreationSecurityCheck();
 }
 
 static void ReportFatalErrorInWorker(const char* location,
diff --git a/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.cpp b/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.cpp
new file mode 100644
index 0000000..a47f565
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.cpp
@@ -0,0 +1,41 @@
+// 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 "bindings/core/v8/WrapperCreationSecurityCheck.h"
+
+#include "bindings/core/v8/WrapperTypeInfo.h"
+
+namespace blink {
+
+WrapperCreationSecurityCheck::SecurityCheckFunction
+    WrapperCreationSecurityCheck::security_check_ = nullptr;
+WrapperCreationSecurityCheck::RethrowExceptionFunction
+    WrapperCreationSecurityCheck::rethrow_exception_ = nullptr;
+
+void WrapperCreationSecurityCheck::SetSecurityCheckFunction(
+    SecurityCheckFunction func) {
+  DCHECK(!security_check_);
+  security_check_ = func;
+}
+
+void WrapperCreationSecurityCheck::SetRethrowExceptionFunction(
+    RethrowExceptionFunction func) {
+  DCHECK(!rethrow_exception_);
+  rethrow_exception_ = func;
+}
+
+bool WrapperCreationSecurityCheck::VerifyContextAccess(
+    v8::Local<v8::Context> creation_context,
+    const WrapperTypeInfo* type) {
+  return (*security_check_)(creation_context, type);
+}
+
+void WrapperCreationSecurityCheck::RethrowCrossContextException(
+    v8::Local<v8::Context> creation_context,
+    const WrapperTypeInfo* type,
+    v8::Local<v8::Value> cross_context_exception) {
+  (*rethrow_exception_)(creation_context, type, cross_context_exception);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.h b/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.h
new file mode 100644
index 0000000..2a25454
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/core/v8/WrapperCreationSecurityCheck.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WrapperCreationSecurityCheck_h
+#define WrapperCreationSecurityCheck_h
+
+#include "core/CoreExport.h"
+#include "platform/wtf/Allocator.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+struct WrapperTypeInfo;
+
+// This class holds pointers to functions that implement creation context access
+// and exception rethrowing logic required by V8WrapperInstantiationScope when
+// creating wrappers.
+class CORE_EXPORT WrapperCreationSecurityCheck {
+  STATIC_ONLY(WrapperCreationSecurityCheck);
+
+ public:
+  using SecurityCheckFunction = bool (*)(v8::Local<v8::Context>,
+                                         const WrapperTypeInfo*);
+  using RethrowExceptionFunction = void (*)(v8::Local<v8::Context>,
+                                            const WrapperTypeInfo*,
+                                            v8::Local<v8::Value>);
+
+  static void SetSecurityCheckFunction(SecurityCheckFunction);
+  static void SetRethrowExceptionFunction(RethrowExceptionFunction);
+
+  static bool VerifyContextAccess(v8::Local<v8::Context> creation_context,
+                                  const WrapperTypeInfo*);
+  static void RethrowCrossContextException(
+      v8::Local<v8::Context> creation_context,
+      const WrapperTypeInfo*,
+      v8::Local<v8::Value> cross_context_exception);
+
+ private:
+  static SecurityCheckFunction security_check_;
+  static RethrowExceptionFunction rethrow_exception_;
+};
+
+}  // namespace blink
+
+#endif  // WrapperCreationSecurityCheck_h
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
index 5c16974..9d908e390 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
@@ -96,8 +96,9 @@
 }
 
 v8::Local<v8::Value> Eval(const String& source, V8TestingScope& scope) {
-  return scope.GetFrame().Script().ExecuteScriptInMainWorldAndReturnValue(
-      source);
+  return scope.GetFrame()
+      .GetScriptController()
+      .ExecuteScriptInMainWorldAndReturnValue(source);
 }
 
 String ToJSON(v8::Local<v8::Object> object, const V8TestingScope& scope) {
diff --git a/third_party/WebKit/Source/bindings/modules/v8/generated.gni b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
index fe159288..77336079 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/generated.gni
+++ b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
@@ -26,8 +26,8 @@
   "$bindings_modules_v8_output_dir/BooleanOrConstrainBooleanParameters.h",
   "$bindings_modules_v8_output_dir/BooleanOrMediaTrackConstraints.cpp",
   "$bindings_modules_v8_output_dir/BooleanOrMediaTrackConstraints.h",
-  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrDictionaryOrHeaders.cpp",
-  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrDictionaryOrHeaders.h",
+  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.cpp",
+  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.h",
   "$bindings_modules_v8_output_dir/ClientOrServiceWorkerOrMessagePort.cpp",
   "$bindings_modules_v8_output_dir/ClientOrServiceWorkerOrMessagePort.h",
   "$bindings_modules_v8_output_dir/CSSImageValueOrHTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmapOrOffscreenCanvas.cpp",
diff --git a/third_party/WebKit/Source/core/animation/Animation.cpp b/third_party/WebKit/Source/core/animation/Animation.cpp
index 2271bc05..a8cde62 100644
--- a/third_party/WebKit/Source/core/animation/Animation.cpp
+++ b/third_party/WebKit/Source/core/animation/Animation.cpp
@@ -37,6 +37,7 @@
 #include "core/dom/DOMNodeIds.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/StyleChangeReason.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/events/AnimationPlaybackEvent.h"
@@ -629,8 +630,9 @@
 
 ScriptPromise Animation::finished(ScriptState* script_state) {
   if (!finished_promise_) {
-    finished_promise_ = new AnimationPromise(
-        script_state->GetExecutionContext(), this, AnimationPromise::kFinished);
+    finished_promise_ =
+        new AnimationPromise(ExecutionContext::From(script_state), this,
+                             AnimationPromise::kFinished);
     if (PlayStateInternal() == kFinished)
       finished_promise_->Resolve(this);
   }
@@ -639,7 +641,7 @@
 
 ScriptPromise Animation::ready(ScriptState* script_state) {
   if (!ready_promise_) {
-    ready_promise_ = new AnimationPromise(script_state->GetExecutionContext(),
+    ready_promise_ = new AnimationPromise(ExecutionContext::From(script_state),
                                           this, AnimationPromise::kReady);
     if (PlayStateInternal() != kPending)
       ready_promise_->Resolve(this);
diff --git a/third_party/WebKit/Source/core/animation/ElementAnimation.h b/third_party/WebKit/Source/core/animation/ElementAnimation.h
index a32fd58..7c33edaf 100644
--- a/third_party/WebKit/Source/core/animation/ElementAnimation.h
+++ b/third_party/WebKit/Source/core/animation/ElementAnimation.h
@@ -42,6 +42,7 @@
 #include "core/animation/TimingInput.h"
 #include "core/dom/Document.h"
 #include "core/dom/Element.h"
+#include "core/dom/ExecutionContext.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/wtf/Allocator.h"
 
@@ -57,7 +58,7 @@
                             double duration,
                             ExceptionState& exception_state) {
     EffectModel* effect = EffectInput::Convert(
-        &element, effect_input, script_state->GetExecutionContext(),
+        &element, effect_input, ExecutionContext::From(script_state),
         exception_state);
     if (exception_state.HadException())
       return nullptr;
@@ -75,7 +76,7 @@
                             const KeyframeEffectOptions& options,
                             ExceptionState& exception_state) {
     EffectModel* effect = EffectInput::Convert(
-        &element, effect_input, script_state->GetExecutionContext(),
+        &element, effect_input, ExecutionContext::From(script_state),
         exception_state);
     if (exception_state.HadException())
       return nullptr;
@@ -95,7 +96,7 @@
                             const DictionarySequenceOrDictionary& effect_input,
                             ExceptionState& exception_state) {
     EffectModel* effect = EffectInput::Convert(
-        &element, effect_input, script_state->GetExecutionContext(),
+        &element, effect_input, ExecutionContext::From(script_state),
         exception_state);
     if (exception_state.HadException())
       return nullptr;
diff --git a/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp b/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
index e0daa9b..737248f2 100644
--- a/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
+++ b/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
@@ -33,6 +33,7 @@
 #include "bindings/core/v8/V8Binding.h"
 #include "core/clipboard/DataObjectItem.h"
 #include "core/clipboard/DataTransfer.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/StringCallback.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/probe/CoreProbes.h"
@@ -83,7 +84,7 @@
   if (!callback || item_->Kind() != DataObjectItem::kStringKind)
     return;
 
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   probe::AsyncTaskScheduled(context, "DataTransferItem.getAsString", callback);
   TaskRunnerHelper::Get(TaskType::kUserInteraction, script_state)
       ->PostTask(BLINK_FROM_HERE,
diff --git a/third_party/WebKit/Source/core/css/FontFace.cpp b/third_party/WebKit/Source/core/css/FontFace.cpp
index 01fca723..8070741 100644
--- a/third_party/WebKit/Source/core/css/FontFace.cpp
+++ b/third_party/WebKit/Source/core/css/FontFace.cpp
@@ -53,6 +53,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/StyleEngine.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/frame/LocalFrame.h"
@@ -415,7 +416,7 @@
 
 ScriptPromise FontFace::FontStatusPromise(ScriptState* script_state) {
   if (!loaded_property_) {
-    loaded_property_ = new LoadedProperty(script_state->GetExecutionContext(),
+    loaded_property_ = new LoadedProperty(ExecutionContext::From(script_state),
                                           this, LoadedProperty::kLoaded);
     if (status_ == kLoaded)
       loaded_property_->Resolve(this);
diff --git a/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp b/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
index 14535bd..67abf4c8 100644
--- a/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
@@ -39,8 +39,8 @@
 
 namespace blink {
 
-static float g_k_off_screen_canvas_em_font_size = 10.0;
-static float g_k_off_screen_canvas_rem_font_size = 16.0;
+static const float kOffScreenCanvasEmFontSize = 10.0;
+static const float kOffScreenCanvasRemFontSize = 16.0;
 
 FilterOperation::OperationType FilterOperationResolver::FilterOperationForType(
     CSSValueID type) {
@@ -217,8 +217,7 @@
   FontDescription font_description;
   Font font(font_description);
   CSSToLengthConversionData::FontSizes font_sizes(
-      g_k_off_screen_canvas_em_font_size, g_k_off_screen_canvas_rem_font_size,
-      &font);
+      kOffScreenCanvasEmFontSize, kOffScreenCanvasRemFontSize, &font);
   CSSToLengthConversionData::ViewportSize viewport_size(1024, 768);
   CSSToLengthConversionData conversion_data(&ComputedStyle::InitialStyle(),
                                             font_sizes, viewport_size, 1);
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 2f287b3..900666f 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -3485,7 +3485,7 @@
   if (!GetFrame())
     return;
 
-  GetFrame()->Script().DisableEval(error_message);
+  GetFrame()->GetScriptController().DisableEval(error_message);
 }
 
 void Document::DidLoadAllImports() {
@@ -4523,7 +4523,7 @@
                              const String& event_type,
                              ExceptionState& exception_state) {
   Event* event = nullptr;
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   for (const auto& factory : EventFactories()) {
     event = factory->Create(execution_context, event_type);
     if (event) {
@@ -4759,7 +4759,7 @@
     if (View() && (was_cross_domain != frame_->IsCrossOriginSubframe()))
       View()->CrossOriginStatusChanged();
 
-    frame_->Script().UpdateSecurityOrigin(GetSecurityOrigin());
+    frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin());
   }
 }
 
@@ -5744,7 +5744,7 @@
 void Document::DidUpdateSecurityOrigin() {
   if (!frame_)
     return;
-  frame_->Script().UpdateSecurityOrigin(GetSecurityOrigin());
+  frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin());
 }
 
 bool Document::IsContextThread() const {
diff --git a/third_party/WebKit/Source/core/dom/MessagePort.cpp b/third_party/WebKit/Source/core/dom/MessagePort.cpp
index 9d83f27..86b58517 100644
--- a/third_party/WebKit/Source/core/dom/MessagePort.cpp
+++ b/third_party/WebKit/Source/core/dom/MessagePort.cpp
@@ -78,7 +78,7 @@
     }
   }
   MessagePortChannelArray channels = MessagePort::DisentanglePorts(
-      script_state->GetExecutionContext(), ports, exception_state);
+      ExecutionContext::From(script_state), ports, exception_state);
   if (exception_state.HadException())
     return;
 
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
index 4622be44..d030c1b 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
@@ -662,7 +662,7 @@
   const ContentSecurityPolicy* csp =
       element_document->GetContentSecurityPolicy();
   bool should_bypass_main_world_csp =
-      (frame->Script().ShouldBypassMainWorldCSP()) ||
+      (frame->GetScriptController().ShouldBypassMainWorldCSP()) ||
       csp->AllowScriptWithHash(source_code.Source(),
                                ContentSecurityPolicy::InlineType::kBlock);
 
@@ -745,7 +745,8 @@
 
   //    2. "Run the classic script given by the script's script."
   // Note: This is where the script is compiled and actually executed.
-  frame->Script().ExecuteScriptInMainWorld(source_code, access_control_status);
+  frame->GetScriptController().ExecuteScriptInMainWorld(source_code,
+                                                        access_control_status);
 
   //    - "module":
   // TODO(hiroshige): Implement this.
diff --git a/third_party/WebKit/Source/core/dom/StyleElement.cpp b/third_party/WebKit/Source/core/dom/StyleElement.cpp
index 5f8bd08..b8cbe5a 100644
--- a/third_party/WebKit/Source/core/dom/StyleElement.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleElement.cpp
@@ -120,7 +120,7 @@
 static bool ShouldBypassMainWorldCSP(const Element& element) {
   // Main world CSP is bypassed within an isolated world.
   LocalFrame* frame = element.GetDocument().GetFrame();
-  if (frame && frame->Script().ShouldBypassMainWorldCSP())
+  if (frame && frame->GetScriptController().ShouldBypassMainWorldCSP())
     return true;
 
   // Main world CSP is bypassed for style elements in user agent shadow DOM.
diff --git a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
index 783e5f80..3cd58de 100644
--- a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
+++ b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
@@ -5,6 +5,7 @@
 #include "core/dom/TaskRunnerHelper.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "platform/WebFrameScheduler.h"
 #include "platform/WebTaskRunner.h"
@@ -72,7 +73,7 @@
 RefPtr<WebTaskRunner> TaskRunnerHelper::Get(TaskType type,
                                             ScriptState* script_state) {
   return Get(type,
-             script_state ? script_state->GetExecutionContext() : nullptr);
+             script_state ? ExecutionContext::From(script_state) : nullptr);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/Event.cpp b/third_party/WebKit/Source/core/events/Event.cpp
index d730f7d2..082f1da 100644
--- a/third_party/WebKit/Source/core/events/Event.cpp
+++ b/third_party/WebKit/Source/core/events/Event.cpp
@@ -23,6 +23,7 @@
 #include "core/events/Event.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/StaticNodeList.h"
 #include "core/events/EventDispatchMediator.h"
 #include "core/events/EventTarget.h"
@@ -138,10 +139,10 @@
 bool Event::legacyReturnValue(ScriptState* script_state) const {
   bool return_value = !defaultPrevented();
   if (return_value) {
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEventGetReturnValueTrue);
   } else {
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEventGetReturnValueFalse);
   }
   return return_value;
@@ -149,10 +150,10 @@
 
 void Event::setLegacyReturnValue(ScriptState* script_state, bool return_value) {
   if (return_value) {
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEventSetReturnValueTrue);
   } else {
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEventSetReturnValueFalse);
   }
   SetDefaultPrevented(!return_value);
diff --git a/third_party/WebKit/Source/core/events/EventListener.h b/third_party/WebKit/Source/core/events/EventListener.h
index 96a5e49..81ce1a64 100644
--- a/third_party/WebKit/Source/core/events/EventListener.h
+++ b/third_party/WebKit/Source/core/events/EventListener.h
@@ -49,8 +49,8 @@
   virtual bool BelongsToTheCurrentWorld(ExecutionContext*) const {
     return false;
   }
+  virtual bool IsAttribute() const { return false; }
 
-  bool IsAttribute() const { return VirtualisAttribute(); }
   ListenerType GetType() const { return type_; }
 
   DEFINE_INLINE_VIRTUAL_TRACE() {}
@@ -59,8 +59,6 @@
   explicit EventListener(ListenerType type) : type_(type) {}
 
  private:
-  virtual bool VirtualisAttribute() const { return false; }
-
   ListenerType type_;
 };
 
diff --git a/third_party/WebKit/Source/core/events/EventTargetTest.cpp b/third_party/WebKit/Source/core/events/EventTargetTest.cpp
index 912c665..c0ac434c 100644
--- a/third_party/WebKit/Source/core/events/EventTargetTest.cpp
+++ b/third_party/WebKit/Source/core/events/EventTargetTest.cpp
@@ -22,7 +22,7 @@
 TEST_F(EventTargetTest, PreventDefaultNotCalled) {
   GetDocument().GetSettings()->SetScriptEnabled(true);
   HistogramTester histogram_tester;
-  GetDocument().GetFrame()->Script().ExecuteScriptInMainWorld(
+  GetDocument().GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
       "window.addEventListener('touchstart', function(e) {}, {});"
       "window.dispatchEvent(new TouchEvent('touchstart', {cancelable: "
       "false}));");
@@ -36,7 +36,7 @@
 TEST_F(EventTargetTest, PreventDefaultCalled) {
   GetDocument().GetSettings()->SetScriptEnabled(true);
   HistogramTester histogram_tester;
-  GetDocument().GetFrame()->Script().ExecuteScriptInMainWorld(
+  GetDocument().GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
       "window.addEventListener('touchstart', function(e) "
       "{e.preventDefault();}, {});"
       "window.dispatchEvent(new TouchEvent('touchstart', {cancelable: "
diff --git a/third_party/WebKit/Source/core/fileapi/Blob.cpp b/third_party/WebKit/Source/core/fileapi/Blob.cpp
index 3ced27e..79f2741c 100644
--- a/third_party/WebKit/Source/core/fileapi/Blob.cpp
+++ b/third_party/WebKit/Source/core/fileapi/Blob.cpp
@@ -201,7 +201,7 @@
   // Dereferencing a Blob that has been closed should result in
   // a network error. Revoke URLs registered against it through
   // its UUID.
-  DOMURL::RevokeObjectUUID(script_state->GetExecutionContext(), Uuid());
+  DOMURL::RevokeObjectUUID(ExecutionContext::From(script_state), Uuid());
 
   // A Blob enters a 'readability state' of closed, where it will report its
   // size as zero. Blob and FileReader operations now throws on
diff --git a/third_party/WebKit/Source/core/fileapi/FileReaderSync.cpp b/third_party/WebKit/Source/core/fileapi/FileReaderSync.cpp
index c64341a..699b57f 100644
--- a/third_party/WebKit/Source/core/fileapi/FileReaderSync.cpp
+++ b/third_party/WebKit/Source/core/fileapi/FileReaderSync.cpp
@@ -33,6 +33,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMArrayBuffer.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
 #include "core/fileapi/FileError.h"
 #include "core/fileapi/FileReaderLoader.h"
@@ -75,7 +76,7 @@
 
   std::unique_ptr<FileReaderLoader> loader =
       FileReaderLoader::Create(FileReaderLoader::kReadAsArrayBuffer, nullptr);
-  StartLoading(script_state->GetExecutionContext(), *loader, *blob,
+  StartLoading(ExecutionContext::From(script_state), *loader, *blob,
                exception_state);
 
   return loader->ArrayBufferResult();
@@ -88,7 +89,7 @@
 
   std::unique_ptr<FileReaderLoader> loader =
       FileReaderLoader::Create(FileReaderLoader::kReadAsBinaryString, nullptr);
-  StartLoading(script_state->GetExecutionContext(), *loader, *blob,
+  StartLoading(ExecutionContext::From(script_state), *loader, *blob,
                exception_state);
   return loader->StringResult();
 }
@@ -102,7 +103,7 @@
   std::unique_ptr<FileReaderLoader> loader =
       FileReaderLoader::Create(FileReaderLoader::kReadAsText, nullptr);
   loader->SetEncoding(encoding);
-  StartLoading(script_state->GetExecutionContext(), *loader, *blob,
+  StartLoading(ExecutionContext::From(script_state), *loader, *blob,
                exception_state);
   return loader->StringResult();
 }
@@ -115,7 +116,7 @@
   std::unique_ptr<FileReaderLoader> loader =
       FileReaderLoader::Create(FileReaderLoader::kReadAsDataURL, nullptr);
   loader->SetDataType(blob->type());
-  StartLoading(script_state->GetExecutionContext(), *loader, *blob,
+  StartLoading(ExecutionContext::From(script_state), *loader, *blob,
                exception_state);
   return loader->StringResult();
 }
diff --git a/third_party/WebKit/Source/core/fileapi/URLFileAPI.cpp b/third_party/WebKit/Source/core/fileapi/URLFileAPI.cpp
index 1e58a3f..271a2db 100644
--- a/third_party/WebKit/Source/core/fileapi/URLFileAPI.cpp
+++ b/third_party/WebKit/Source/core/fileapi/URLFileAPI.cpp
@@ -19,7 +19,7 @@
                                    Blob* blob,
                                    ExceptionState& exception_state) {
   DCHECK(blob);
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
 
   if (blob->isClosed()) {
@@ -38,7 +38,7 @@
 // static
 void URLFileAPI::revokeObjectURL(ScriptState* script_state,
                                  const String& url_string) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
 
   KURL url(KURL(), url_string);
diff --git a/third_party/WebKit/Source/core/frame/DOMTimerTest.cpp b/third_party/WebKit/Source/core/frame/DOMTimerTest.cpp
index a8d0c332..eb969169 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimerTest.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimerTest.cpp
@@ -53,7 +53,7 @@
   v8::Local<v8::Value> EvalExpression(const char* expr) {
     return GetDocument()
         .GetFrame()
-        ->Script()
+        ->GetScriptController()
         .ExecuteScriptInMainWorldAndReturnValue(ScriptSourceCode(expr));
   }
 
@@ -71,7 +71,8 @@
 
   void ExecuteScriptAndWaitUntilIdle(const char* script_text) {
     ScriptSourceCode script(script_text);
-    GetDocument().GetFrame()->Script().ExecuteScriptInMainWorld(script);
+    GetDocument().GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
+        script);
     platform_->RunUntilIdle();
   }
 };
diff --git a/third_party/WebKit/Source/core/frame/History.cpp b/third_party/WebKit/Source/core/frame/History.cpp
index d878e9e..c053a6e 100644
--- a/third_party/WebKit/Source/core/frame/History.cpp
+++ b/third_party/WebKit/Source/core/frame/History.cpp
@@ -28,6 +28,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/LocalFrameClient.h"
 #include "core/loader/DocumentLoader.h"
@@ -136,7 +137,7 @@
     return;
 
   DCHECK(IsMainThread());
-  Document* active_document = ToDocument(script_state->GetExecutionContext());
+  Document* active_document = ToDocument(ExecutionContext::From(script_state));
   if (!active_document)
     return;
 
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index e5f88b2..e3135f1 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -356,7 +356,7 @@
   if (!GetFrame())
     return document_;
 
-  GetFrame()->Script().UpdateDocument();
+  GetFrame()->GetScriptController().UpdateDocument();
   document_->UpdateViewportDescription();
 
   if (GetFrame()->GetPage() && GetFrame()->View()) {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index 71d6800..8f18e6b 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -363,7 +363,7 @@
   visitor->Trace(view_);
   visitor->Trace(dom_window_);
   visitor->Trace(page_popup_owner_);
-  visitor->Trace(script_);
+  visitor->Trace(script_controller_);
   visitor->Trace(editor_);
   visitor->Trace(spell_checker_);
   visitor->Trace(selection_);
@@ -443,7 +443,7 @@
   Client()->WillBeDetached();
   // Notify ScriptController that the frame is closing, since its cleanup ends
   // up calling back to LocalFrameClient via WindowProxy.
-  Script().ClearForClose();
+  GetScriptController().ClearForClose();
   SetView(nullptr);
 
   page_->GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
@@ -537,7 +537,7 @@
 
 void LocalFrame::SetDOMWindow(LocalDOMWindow* dom_window) {
   if (dom_window)
-    Script().ClearWindowProxy();
+    GetScriptController().ClearWindowProxy();
 
   if (this->DomWindow())
     this->DomWindow()->Reset();
@@ -882,7 +882,7 @@
           client->GetFrameBlameContext())),
       loader_(this),
       navigation_scheduler_(NavigationScheduler::Create(this)),
-      script_(ScriptController::Create(
+      script_controller_(ScriptController::Create(
           *this,
           *static_cast<LocalWindowProxyManager*>(GetWindowProxyManager()))),
       editor_(Editor::Create(*this)),
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.h b/third_party/WebKit/Source/core/frame/LocalFrame.h
index 3b250f3b..97a5400 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.h
@@ -146,7 +146,7 @@
   NavigationScheduler& GetNavigationScheduler() const;
   FrameSelection& Selection() const;
   InputMethodController& GetInputMethodController() const;
-  ScriptController& Script() const;
+  ScriptController& GetScriptController() const;
   SpellChecker& GetSpellChecker() const;
   FrameConsole& Console() const;
 
@@ -263,7 +263,7 @@
   // Usually 0. Non-null if this is the top frame of PagePopup.
   Member<Element> page_popup_owner_;
 
-  const Member<ScriptController> script_;
+  const Member<ScriptController> script_controller_;
   const Member<Editor> editor_;
   const Member<SpellChecker> spell_checker_;
   const Member<FrameSelection> selection_;
@@ -298,8 +298,8 @@
   return view_.Get();
 }
 
-inline ScriptController& LocalFrame::Script() const {
-  return *script_;
+inline ScriptController& LocalFrame::GetScriptController() const {
+  return *script_controller_;
 }
 
 inline FrameSelection& LocalFrame::Selection() const {
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
index be7f3e81..af63db5 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
@@ -1481,8 +1481,11 @@
     const ExecutionContext* context) {
   if (context && context->IsDocument()) {
     const Document* document = ToDocument(context);
-    if (document->GetFrame())
-      return document->GetFrame()->Script().ShouldBypassMainWorldCSP();
+    if (document->GetFrame()) {
+      return document->GetFrame()
+          ->GetScriptController()
+          .ShouldBypassMainWorldCSP();
+    }
   }
   return false;
 }
diff --git a/third_party/WebKit/Source/core/html/FormData.cpp b/third_party/WebKit/Source/core/html/FormData.cpp
index ddd0b675..e3eb0c8 100644
--- a/third_party/WebKit/Source/core/html/FormData.cpp
+++ b/third_party/WebKit/Source/core/html/FormData.cpp
@@ -31,6 +31,7 @@
 #include "core/html/FormData.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
 #include "core/fileapi/File.h"
 #include "core/frame/UseCounter.h"
@@ -106,7 +107,7 @@
                       Blob* blob,
                       const String& filename) {
   if (!blob) {
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kFormDataAppendNull);
   }
   append(name, blob, filename);
diff --git a/third_party/WebKit/Source/core/html/HTMLDocument.cpp b/third_party/WebKit/Source/core/html/HTMLDocument.cpp
index 4495202..67793a7 100644
--- a/third_party/WebKit/Source/core/html/HTMLDocument.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLDocument.cpp
@@ -154,7 +154,7 @@
     return;
   map.insert(name);
   if (LocalFrame* f = GetFrame()) {
-    f->Script()
+    f->GetScriptController()
         .WindowProxy(DOMWrapperWorld::MainWorld())
         ->NamedItemAdded(this, name);
   }
@@ -166,7 +166,7 @@
     return;
   map.erase(name);
   if (LocalFrame* f = GetFrame()) {
-    f->Script()
+    f->GetScriptController()
         .WindowProxy(DOMWrapperWorld::MainWorld())
         ->NamedItemRemoved(this, name);
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
index d14303a9..7b890bb 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
@@ -443,8 +443,10 @@
   }
 
   if (submission->Action().ProtocolIsJavaScript()) {
-    GetDocument().GetFrame()->Script().ExecuteScriptIfJavaScriptURL(
-        submission->Action(), this);
+    GetDocument()
+        .GetFrame()
+        ->GetScriptController()
+        .ExecuteScriptIfJavaScriptURL(submission->Action(), this);
     return;
   }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
index 6c41556..f5728ab 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
@@ -117,7 +117,7 @@
   if (ContentFrame()->Owner()->GetSandboxFlags() & kSandboxOrigin)
     return;
   ToLocalFrame(ContentFrame())
-      ->Script()
+      ->GetScriptController()
       .ExecuteScriptIfJavaScriptURL(script_url, this);
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementSandbox.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElementSandbox.cpp
index b370dcd..24fe064 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementSandbox.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElementSandbox.cpp
@@ -10,17 +10,17 @@
 
 namespace {
 
-const char* g_k_supported_tokens[] = {"allow-forms",
-                                      "allow-modals",
-                                      "allow-pointer-lock",
-                                      "allow-popups",
-                                      "allow-popups-to-escape-sandbox",
-                                      "allow-same-origin",
-                                      "allow-scripts",
-                                      "allow-top-navigation"};
+const char* const kSupportedTokens[] = {"allow-forms",
+                                        "allow-modals",
+                                        "allow-pointer-lock",
+                                        "allow-popups",
+                                        "allow-popups-to-escape-sandbox",
+                                        "allow-same-origin",
+                                        "allow-scripts",
+                                        "allow-top-navigation"};
 
 bool IsTokenSupported(const AtomicString& token) {
-  for (const char* supported_token : g_k_supported_tokens) {
+  for (const char* supported_token : kSupportedTokens) {
     if (token == supported_token)
       return true;
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
index 7e1a51a..780bf147 100644
--- a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
@@ -378,8 +378,10 @@
     else
       plugin = PluginWidget();
 
-    if (plugin)
-      plugin_wrapper_ = frame->Script().CreatePluginWrapper(*plugin);
+    if (plugin) {
+      plugin_wrapper_ =
+          frame->GetScriptController().CreatePluginWrapper(*plugin);
+    }
   }
   return plugin_wrapper_.Get();
 }
diff --git a/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp b/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
index c4fade2..c930409 100644
--- a/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
+++ b/third_party/WebKit/Source/core/html/media/HTMLMediaElementControlsList.cpp
@@ -14,8 +14,8 @@
 const char kNoFullscreen[] = "nofullscreen";
 const char kNoRemotePlayback[] = "noremoteplayback";
 
-const char* g_k_supported_tokens[] = {kNoDownload, kNoFullscreen,
-                                      kNoRemotePlayback};
+const char* const kSupportedTokens[] = {kNoDownload, kNoFullscreen,
+                                        kNoRemotePlayback};
 
 }  // namespace
 
@@ -34,7 +34,7 @@
 bool HTMLMediaElementControlsList::ValidateTokenValue(
     const AtomicString& token_value,
     ExceptionState&) const {
-  for (const char* supported_token : g_k_supported_tokens) {
+  for (const char* supported_token : kSupportedTokens) {
     if (token_value == supported_token)
       return true;
   }
diff --git a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
index 3c2a1a7..f273bc8 100644
--- a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
+++ b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
@@ -130,7 +130,7 @@
     const ImageBitmapOptions& options,
     ExceptionState& exception_state) {
   UseCounter::Feature feature = UseCounter::kCreateImageBitmap;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   ImageBitmapSource* bitmap_source_internal = ToImageBitmapSourceInternal(
       bitmap_source, exception_state, options, false);
   if (!bitmap_source_internal)
@@ -150,7 +150,7 @@
     const ImageBitmapOptions& options,
     ExceptionState& exception_state) {
   UseCounter::Feature feature = UseCounter::kCreateImageBitmap;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   ImageBitmapSource* bitmap_source_internal = ToImageBitmapSourceInternal(
       bitmap_source, exception_state, options, true);
   if (!bitmap_source_internal)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
index 6d7112c..f0c0512 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
@@ -245,7 +245,7 @@
       MaybeEncodeTextContent(
           cached_resource->ResourceBuffer()
               ? ToScriptResource(cached_resource)->DecodedText()
-              : ToScriptResource(cached_resource)->Script(),
+              : ToScriptResource(cached_resource)->SourceText(),
           cached_resource->ResourceBuffer(), result, base64_encoded);
       return true;
     default:
@@ -648,11 +648,13 @@
       auto script = scripts->at(i);
       String script_text;
       if (script.second->asString(&script_text))
-        frame->Script().ExecuteScriptInMainWorld(script_text);
+        frame->GetScriptController().ExecuteScriptInMainWorld(script_text);
     }
   }
-  if (!script_to_evaluate_on_load_once_.IsEmpty())
-    frame->Script().ExecuteScriptInMainWorld(script_to_evaluate_on_load_once_);
+  if (!script_to_evaluate_on_load_once_.IsEmpty()) {
+    frame->GetScriptController().ExecuteScriptInMainWorld(
+        script_to_evaluate_on_load_once_);
+  }
 }
 
 void InspectorPageAgent::DomContentLoadedEventFired(LocalFrame* frame) {
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
index 7a41ab9..e5e95aad 100644
--- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
@@ -161,8 +161,8 @@
   context_info.origin = ToV8InspectorStringView(origin_string);
   context_info.auxData = ToV8InspectorStringView(aux_data);
   context_info.hasMemoryOnConsole =
-      script_state->GetExecutionContext() &&
-      script_state->GetExecutionContext()->IsDocument();
+      ExecutionContext::From(script_state) &&
+      ExecutionContext::From(script_state)->IsDocument();
   GetV8Inspector()->contextCreated(context_info);
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextTest.cpp b/third_party/WebKit/Source/core/layout/LayoutTextTest.cpp
index e6424f4d..1764204 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextTest.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextTest.cpp
@@ -24,24 +24,24 @@
   }
 };
 
-const char* g_k_taco_text = "Los Compadres Taco Truck";
+const char kTacoText[] = "Los Compadres Taco Truck";
 
 }  // namespace
 
 TEST_F(LayoutTextTest, WidthZeroFromZeroLength) {
-  SetBasicBody(g_k_taco_text);
+  SetBasicBody(kTacoText);
   ASSERT_EQ(0, GetBasicText()->Width(0u, 0u, LayoutUnit(), TextDirection::kLtr,
                                      false));
 }
 
 TEST_F(LayoutTextTest, WidthMaxFromZeroLength) {
-  SetBasicBody(g_k_taco_text);
+  SetBasicBody(kTacoText);
   ASSERT_EQ(0, GetBasicText()->Width(std::numeric_limits<unsigned>::max(), 0u,
                                      LayoutUnit(), TextDirection::kLtr, false));
 }
 
 TEST_F(LayoutTextTest, WidthZeroFromMaxLength) {
-  SetBasicBody(g_k_taco_text);
+  SetBasicBody(kTacoText);
   float width = GetBasicText()->Width(0u, std::numeric_limits<unsigned>::max(),
                                       LayoutUnit(), TextDirection::kLtr, false);
   // Width may vary by platform and we just want to make sure it's something
@@ -51,7 +51,7 @@
 }
 
 TEST_F(LayoutTextTest, WidthMaxFromMaxLength) {
-  SetBasicBody(g_k_taco_text);
+  SetBasicBody(kTacoText);
   ASSERT_EQ(0, GetBasicText()->Width(std::numeric_limits<unsigned>::max(),
                                      std::numeric_limits<unsigned>::max(),
                                      LayoutUnit(), TextDirection::kLtr, false));
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 43f5f01..7a477fc 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -3429,7 +3429,7 @@
 // Only used for performance benchmark testing. Intended to be a
 // sufficiently-unique element id name to allow picking out the target element
 // for invalidation.
-static const char* g_k_test_paint_invalidation_target_name =
+static const char kTestPaintInvalidationTargetName[] =
     "blinkPaintInvalidationTarget";
 
 void CompositedLayerMapping::InvalidateTargetElementForTesting() {
@@ -3437,7 +3437,7 @@
   // microbenchmark on the cost of paint with a partial invalidation.
   Element* target_element =
       owning_layer_.GetLayoutObject().GetDocument().GetElementById(
-          AtomicString(g_k_test_paint_invalidation_target_name));
+          AtomicString(kTestPaintInvalidationTargetName));
   // TODO(wkorman): If we don't find the expected target element, we could
   // consider walking to the first leaf node so that the partial-invalidation
   // benchmark mode still provides some value when running on generic pages.
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 7026ad9..8e34465b 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -549,10 +549,8 @@
   }
 
   // TODO(kojii): Check if the line box width should be content or available.
-  container_builder_.SetInlineSize(max_inline_size_)
-      .SetInlineOverflow(max_inline_size_)
-      .SetBlockSize(content_size_)
-      .SetBlockOverflow(content_size_);
+  NGLogicalSize size(max_inline_size_, content_size_);
+  container_builder_.SetSize(size).SetOverflowSize(size);
 
   return container_builder_.ToBoxFragment();
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index 741ed08..143ac82a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -160,7 +160,7 @@
 
   builder_.SetDirection(constraint_space_->Direction());
   builder_.SetWritingMode(constraint_space_->WritingMode());
-  builder_.SetInlineSize(size.inline_size).SetBlockSize(size.block_size);
+  builder_.SetSize(size);
 
   NGBlockChildIterator child_iterator(Node()->FirstChild(), BreakToken());
   NGBlockChildIterator::Entry entry = child_iterator.NextChild();
@@ -262,7 +262,7 @@
   }
   builder_.SetEndMarginStrut(curr_margin_strut_);
 
-  builder_.SetInlineOverflow(max_inline_size_).SetBlockOverflow(content_size_);
+  builder_.SetOverflowSize(NGLogicalSize(max_inline_size_, content_size_));
 
   if (ConstraintSpace().HasBlockFragmentation())
     FinalizeForFragmentation();
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
index 35c80b68..ae698eb 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
@@ -354,17 +354,18 @@
   } else {
     layout_box_->ForceLayout();
   }
-  LayoutRect overflow = layout_box_->LayoutOverflowRect();
+  NGLogicalSize box_size(layout_box_->LogicalWidth(),
+                         layout_box_->LogicalHeight());
   // TODO(layout-ng): This does not handle writing modes correctly (for
-  // overflow)
+  // overflow).
+  NGLogicalSize overflow_size(layout_box_->LayoutOverflowRect().Width(),
+                              layout_box_->LayoutOverflowRect().Height());
   NGFragmentBuilder builder(NGPhysicalFragment::kFragmentBox, this);
-  builder.SetInlineSize(layout_box_->LogicalWidth())
-      .SetBlockSize(layout_box_->LogicalHeight())
+  builder.SetSize(box_size)
       .SetDirection(layout_box_->StyleRef().Direction())
       .SetWritingMode(
           FromPlatformWritingMode(layout_box_->StyleRef().GetWritingMode()))
-      .SetInlineOverflow(overflow.Width())
-      .SetBlockOverflow(overflow.Height());
+      .SetOverflowSize(overflow_size);
   return builder.ToBoxFragment();
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
index 3234ff1f..d378a40 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -35,8 +35,8 @@
   return *this;
 }
 
-NGFragmentBuilder& NGFragmentBuilder::SetInlineSize(LayoutUnit size) {
-  size_.inline_size = size;
+NGFragmentBuilder& NGFragmentBuilder::SetSize(const NGLogicalSize& size) {
+  size_ = size;
   return *this;
 }
 
@@ -45,8 +45,9 @@
   return *this;
 }
 
-NGFragmentBuilder& NGFragmentBuilder::SetInlineOverflow(LayoutUnit size) {
-  overflow_.inline_size = size;
+NGFragmentBuilder& NGFragmentBuilder::SetOverflowSize(
+    const NGLogicalSize& size) {
+  overflow_ = size;
   return *this;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
index 6d0ec74..c665a5b 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -27,11 +27,11 @@
   NGFragmentBuilder& SetWritingMode(NGWritingMode);
   NGFragmentBuilder& SetDirection(TextDirection);
 
-  NGFragmentBuilder& SetInlineSize(LayoutUnit);
+  NGFragmentBuilder& SetSize(const NGLogicalSize&);
   NGFragmentBuilder& SetBlockSize(LayoutUnit);
   NGLogicalSize Size() const { return size_; }
 
-  NGFragmentBuilder& SetInlineOverflow(LayoutUnit);
+  NGFragmentBuilder& SetOverflowSize(const NGLogicalSize&);
   NGFragmentBuilder& SetBlockOverflow(LayoutUnit);
 
   NGFragmentBuilder& AddChild(RefPtr<NGLayoutResult>, const NGLogicalOffset&);
@@ -64,7 +64,7 @@
   //     builder->AddChild(fragment)
   // end
   //
-  // builder->SetInlineSize/SetBlockSize
+  // builder->SetSize
   //
   // Part 2: Out-of-flow layout part positions out-of-flow descendants.
   //
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index bb6009d..ec6f92f51 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -767,7 +767,7 @@
   // FIXME: Convert this to check the isolated world's Content Security Policy
   // once webkit.org/b/104520 is solved.
   bool should_bypass_main_world_csp =
-      GetFrame()->Script().ShouldBypassMainWorldCSP() ||
+      GetFrame()->GetScriptController().ShouldBypassMainWorldCSP() ||
       options.content_security_policy_option ==
           kDoNotCheckContentSecurityPolicy;
 
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 832657b..a6a1eb6 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -329,7 +329,7 @@
   if (frame_->View())
     frame_->View()->Clear();
 
-  frame_->Script().EnableEval();
+  frame_->GetScriptController().EnableEval();
 
   frame_->GetNavigationScheduler().Cancel();
 
@@ -740,7 +740,7 @@
     return true;
 
   KURL url = request.GetResourceRequest().Url();
-  if (frame_->Script().ExecuteScriptIfJavaScriptURL(url, nullptr))
+  if (frame_->GetScriptController().ExecuteScriptIfJavaScriptURL(url, nullptr))
     return false;
 
   if (!request.OriginDocument()->GetSecurityOrigin()->CanDisplay(url)) {
@@ -1601,7 +1601,7 @@
   Settings* settings = frame_->GetSettings();
   if (settings && settings->GetForceMainWorldInitialization()) {
     // Forcibly instantiate WindowProxy.
-    frame_->Script().WindowProxy(DOMWrapperWorld::MainWorld());
+    frame_->GetScriptController().WindowProxy(DOMWrapperWorld::MainWorld());
   }
   probe::didClearDocumentOfWindowObject(frame_);
 
diff --git a/third_party/WebKit/Source/core/loader/ImageLoader.cpp b/third_party/WebKit/Source/core/loader/ImageLoader.cpp
index a2533b1..20724aa1 100644
--- a/third_party/WebKit/Source/core/loader/ImageLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/ImageLoader.cpp
@@ -81,7 +81,7 @@
       loader->GetElement()
           ->GetDocument()
           .GetFrame()
-          ->Script()
+          ->GetScriptController()
           .ShouldBypassMainWorldCSP())
     return ImageLoader::kBypassMainWorldCSP;
   return ImageLoader::kDoNotBypassMainWorldCSP;
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoader.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoader.cpp
index ce8f50df..392276bc 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleScriptLoader.cpp
@@ -198,7 +198,7 @@
   }
 
   // Step 8. Let source text be the result of UTF-8 decoding response's body.
-  String source_text = GetResource()->Script();
+  String source_text = GetResource()->SourceText();
 
   AccessControlStatus access_control_status =
       GetResource()->CalculateAccessControlStatus(
diff --git a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
index c53f90b2..c811db97 100644
--- a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
@@ -77,26 +77,26 @@
   Resource::OnMemoryDump(level_of_detail, memory_dump);
   const String name = GetMemoryDumpName() + "/decoded_script";
   auto dump = memory_dump->CreateMemoryAllocatorDump(name);
-  dump->AddScalar("size", "bytes", script_.CharactersSizeInBytes());
+  dump->AddScalar("size", "bytes", source_text_.CharactersSizeInBytes());
   memory_dump->AddSuballocation(
       dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
 }
 
-const String& ScriptResource::Script() {
+const String& ScriptResource::SourceText() {
   DCHECK(IsLoaded());
 
-  if (script_.IsNull() && Data()) {
-    String script = DecodedText();
+  if (source_text_.IsNull() && Data()) {
+    String source_text = DecodedText();
     ClearData();
-    SetDecodedSize(script.CharactersSizeInBytes());
-    script_ = AtomicString(script);
+    SetDecodedSize(source_text.CharactersSizeInBytes());
+    source_text_ = AtomicString(source_text);
   }
 
-  return script_;
+  return source_text_;
 }
 
 void ScriptResource::DestroyDecodedDataForFailedRevalidation() {
-  script_ = AtomicString();
+  source_text_ = AtomicString();
 }
 
 // static
diff --git a/third_party/WebKit/Source/core/loader/resource/ScriptResource.h b/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
index e376ee7..5bd6a8be 100644
--- a/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
+++ b/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
@@ -72,7 +72,7 @@
 
   void DestroyDecodedDataForFailedRevalidation() override;
 
-  const String& Script();
+  const String& SourceText();
 
   static bool MimeTypeAllowedByNosniff(const ResourceResponse&);
 
@@ -94,7 +94,7 @@
                  const ResourceLoaderOptions&,
                  const String& charset);
 
-  AtomicString script_;
+  AtomicString source_text_;
 };
 
 DEFINE_RESOURCE_TYPE_CASTS(Script);
diff --git a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
index 14ac6bd..aed10b7 100644
--- a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMArrayBufferView.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/mojo/MojoCreateSharedBufferResult.h"
 #include "core/mojo/MojoDiscardDataOptions.h"
 #include "core/mojo/MojoDuplicateBufferHandleOptions.h"
@@ -42,7 +43,7 @@
                                const MojoHandleSignals& signals,
                                MojoWatchCallback* callback) {
   return MojoWatcher::Create(handle_.get(), signals, callback,
-                             script_state->GetExecutionContext());
+                             ExecutionContext::From(script_state));
 }
 
 MojoResult MojoHandle::writeMessage(
diff --git a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
index 428858a4..e51a3619 100644
--- a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
+++ b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
 #include "core/frame/ImageBitmap.h"
 #include "core/html/ImageData.h"
@@ -337,8 +338,8 @@
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
 
   Document* document =
-      script_state->GetExecutionContext()->IsDocument()
-          ? static_cast<Document*>(script_state->GetExecutionContext())
+      ExecutionContext::From(script_state)->IsDocument()
+          ? static_cast<Document*>(ExecutionContext::From(script_state))
           : nullptr;
 
   CanvasAsyncBlobCreator* async_creator = CanvasAsyncBlobCreator::Create(
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 756683f..941a30c 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -546,7 +546,8 @@
                 .StateMachine()
                 ->CommittedFirstRealDocumentLoad()) {
           // Forcibly instantiate WindowProxy.
-          local_frame->Script().WindowProxy(DOMWrapperWorld::MainWorld());
+          local_frame->GetScriptController().WindowProxy(
+              DOMWrapperWorld::MainWorld());
         }
       }
     } break;
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
index 72bb9c45..1c3f3b4 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -499,6 +499,10 @@
   // tile.
   FloatRect image_tile;
   if (info.should_paint_image) {
+    // Avoid image shaders when printing (poorly supported in PDF).
+    if (info.is_rounded_fill && paint_info.IsPrinting())
+      return false;
+
     DCHECK(!geometry);
     geometry.emplace();
     geometry->Calculate(obj, background_object, paint_info.PaintContainer(),
diff --git a/third_party/WebKit/Source/core/streams/UnderlyingSourceBase.h b/third_party/WebKit/Source/core/streams/UnderlyingSourceBase.h
index ccdd413e..d04c1885 100644
--- a/third_party/WebKit/Source/core/streams/UnderlyingSourceBase.h
+++ b/third_party/WebKit/Source/core/streams/UnderlyingSourceBase.h
@@ -12,6 +12,7 @@
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/CoreExport.h"
 #include "core/dom/ContextLifecycleObserver.h"
+#include "core/dom/ExecutionContext.h"
 #include "platform/heap/GarbageCollected.h"
 #include "platform/heap/Handle.h"
 
@@ -50,7 +51,7 @@
 
  protected:
   explicit UnderlyingSourceBase(ScriptState* script_state)
-      : ContextLifecycleObserver(script_state->GetExecutionContext()) {}
+      : ContextLifecycleObserver(ExecutionContext::From(script_state)) {}
 
   ReadableStreamController* Controller() const { return controller_; }
 
diff --git a/third_party/WebKit/Source/core/svg/SVGAnimationElement.cpp b/third_party/WebKit/Source/core/svg/SVGAnimationElement.cpp
index a5ddadde..7daff2e 100644
--- a/third_party/WebKit/Source/core/svg/SVGAnimationElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGAnimationElement.cpp
@@ -59,7 +59,7 @@
       if (i < last)
         goto fail;
     } else {
-      parse_list[i] = parse_list[i].StripWhiteSpace();
+      parse_list[i] = parse_list[i].StripWhiteSpace(IsHTMLSpace<UChar>);
       result.push_back(parse_list[i]);
     }
   }
diff --git a/third_party/WebKit/Source/core/testing/DictionaryTest.cpp b/third_party/WebKit/Source/core/testing/DictionaryTest.cpp
index 2eab6a7e..f85dd44 100644
--- a/third_party/WebKit/Source/core/testing/DictionaryTest.cpp
+++ b/third_party/WebKit/Source/core/testing/DictionaryTest.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/V8ObjectBuilder.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/testing/InternalDictionary.h"
 #include "core/testing/InternalDictionaryDerived.h"
 #include "core/testing/InternalDictionaryDerivedDerived.h"
@@ -173,7 +174,7 @@
     Dictionary iterable,
     ExceptionState& exception_state) const {
   StringBuilder result;
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DictionaryIterator iterator = iterable.GetIterator(execution_context);
   if (iterator.IsNull())
     return g_empty_string;
diff --git a/third_party/WebKit/Source/core/testing/OriginTrialsTest.cpp b/third_party/WebKit/Source/core/testing/OriginTrialsTest.cpp
index e1e46f89..d2e920b 100644
--- a/third_party/WebKit/Source/core/testing/OriginTrialsTest.cpp
+++ b/third_party/WebKit/Source/core/testing/OriginTrialsTest.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ExceptionState.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/origin_trials/OriginTrials.h"
 
 namespace blink {
@@ -23,7 +24,7 @@
                                          ExceptionState& exception_state) {
   String error_message;
   if (!OriginTrials::originTrialsSampleAPIEnabled(
-          script_state->GetExecutionContext())) {
+          ExecutionContext::From(script_state))) {
     exception_state.ThrowDOMException(
         kNotSupportedError,
         "The Origin Trials Sample API has not been enabled in this context");
diff --git a/third_party/WebKit/Source/core/testing/WorkerInternals.cpp b/third_party/WebKit/Source/core/testing/WorkerInternals.cpp
index 2eba885..42ee5394 100644
--- a/third_party/WebKit/Source/core/testing/WorkerInternals.cpp
+++ b/third_party/WebKit/Source/core/testing/WorkerInternals.cpp
@@ -5,6 +5,7 @@
 #include "core/testing/WorkerInternals.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/Deprecation.h"
 #include "core/frame/UseCounter.h"
 #include "core/testing/OriginTrialsTest.h"
@@ -21,13 +22,13 @@
 
 void WorkerInternals::countFeature(ScriptState* script_state,
                                    uint32_t feature) {
-  UseCounter::Count(script_state->GetExecutionContext(),
+  UseCounter::Count(ExecutionContext::From(script_state),
                     static_cast<UseCounter::Feature>(feature));
 }
 
 void WorkerInternals::countDeprecation(ScriptState* script_state,
                                        uint32_t feature) {
-  Deprecation::CountDeprecation(script_state->GetExecutionContext(),
+  Deprecation::CountDeprecation(ExecutionContext::From(script_state),
                                 static_cast<UseCounter::Feature>(feature));
 }
 
diff --git a/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp b/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
index d92af6c..c17d5d6 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/PerformanceObserverCallback.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/testing/DummyPageHolder.h"
 #include "core/testing/NullExecutionContext.h"
@@ -46,8 +47,8 @@
         v8::Function::New(script_state->GetContext(), nullptr).ToLocalChecked();
     base_ = new TestPerformanceBase(script_state);
     cb_ = PerformanceObserverCallback::Create(script_state, callback);
-    observer_ = PerformanceObserver::Create(script_state->GetExecutionContext(),
-                                            base_, cb_);
+    observer_ = PerformanceObserver::Create(
+        ExecutionContext::From(script_state), base_, cb_);
   }
 
   void SetUp() override {
diff --git a/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp b/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
index 81b805c..89e6ed4 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/PerformanceObserverCallback.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/timing/Performance.h"
 #include "core/timing/PerformanceBase.h"
@@ -33,8 +34,8 @@
         v8::Function::New(script_state->GetContext(), nullptr).ToLocalChecked();
     base_ = new MockPerformanceBase(script_state);
     cb_ = PerformanceObserverCallback::Create(script_state, callback);
-    observer_ = PerformanceObserver::Create(script_state->GetExecutionContext(),
-                                            base_, cb_);
+    observer_ = PerformanceObserver::Create(
+        ExecutionContext::From(script_state), base_, cb_);
   }
 
   bool IsRegistered() { return observer_->is_registered_; }
diff --git a/third_party/WebKit/Source/core/timing/SharedWorkerPerformance.cpp b/third_party/WebKit/Source/core/timing/SharedWorkerPerformance.cpp
index e96d521..1b62a30 100644
--- a/third_party/WebKit/Source/core/timing/SharedWorkerPerformance.cpp
+++ b/third_party/WebKit/Source/core/timing/SharedWorkerPerformance.cpp
@@ -60,7 +60,7 @@
 double SharedWorkerPerformance::workerStart(ScriptState* script_state,
                                             SharedWorker& shared_worker) {
   return SharedWorkerPerformance::From(shared_worker)
-      .GetWorkerStart(script_state->GetExecutionContext(), shared_worker);
+      .GetWorkerStart(ExecutionContext::From(script_state), shared_worker);
 }
 
 double SharedWorkerPerformance::GetWorkerStart(ExecutionContext* context,
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp
index 67f3de6..8250b19 100644
--- a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp
@@ -34,6 +34,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/SerializedScriptValue.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/csp/ContentSecurityPolicy.h"
 #include "core/origin_trials/OriginTrialContext.h"
 #include "core/workers/DedicatedWorkerThread.h"
@@ -92,7 +93,7 @@
     ExceptionState& exception_state) {
   // Disentangle the port in preparation for sending it to the remote context.
   MessagePortChannelArray channels = MessagePort::DisentanglePorts(
-      script_state->GetExecutionContext(), ports, exception_state);
+      ExecutionContext::From(script_state), ports, exception_state);
   if (exception_state.HadException())
     return;
   WorkerObjectProxy().PostMessageToWorkerObject(std::move(message),
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp
index bf5e32b..2060806 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp
@@ -7,6 +7,7 @@
 #include <memory>
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/events/MessageEvent.h"
 #include "core/probe/CoreProbes.h"
 #include "core/workers/InProcessWorkerMessagingProxy.h"
@@ -32,7 +33,7 @@
   DCHECK(context_proxy_);
   // Disentangle the port in preparation for sending it to the remote context.
   MessagePortChannelArray channels = MessagePort::DisentanglePorts(
-      script_state->GetExecutionContext(), ports, exception_state);
+      ExecutionContext::From(script_state), ports, exception_state);
   if (exception_state.HadException())
     return;
   context_proxy_->PostMessageToWorkerGlobalScope(std::move(message),
@@ -96,9 +97,9 @@
   } else {
     context_proxy_->StartWorkerGlobalScope(
         script_loader_->Url(), GetExecutionContext()->UserAgent(),
-        script_loader_->Script(), script_loader_->GetReferrerPolicy());
+        script_loader_->SourceText(), script_loader_->GetReferrerPolicy());
     probe::scriptImported(GetExecutionContext(), script_loader_->Identifier(),
-                          script_loader_->Script());
+                          script_loader_->SourceText());
   }
   script_loader_ = nullptr;
 }
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
index f608112..d6a3a33bb 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -210,7 +210,7 @@
     }
 
     probe::scriptImported(&execution_context, script_loader->Identifier(),
-                          script_loader->Script());
+                          script_loader->SourceText());
 
     ErrorEvent* error_event = nullptr;
     std::unique_ptr<Vector<char>> cached_meta_data(
@@ -218,13 +218,13 @@
     CachedMetadataHandler* handler(CreateWorkerScriptCachedMetadataHandler(
         complete_url, cached_meta_data.get()));
     GetThread()->GetWorkerReportingProxy().WillEvaluateImportedScript(
-        script_loader->Script().length(),
+        script_loader->SourceText().length(),
         script_loader->CachedMetadata()
             ? script_loader->CachedMetadata()->size()
             : 0);
-    script_controller_->Evaluate(
-        ScriptSourceCode(script_loader->Script(), script_loader->ResponseURL()),
-        &error_event, handler, v8_cache_options_);
+    script_controller_->Evaluate(ScriptSourceCode(script_loader->SourceText(),
+                                                  script_loader->ResponseURL()),
+                                 &error_event, handler, v8_cache_options_);
     if (error_event) {
       script_controller_->RethrowExceptionFromImportedScript(error_event,
                                                              exception_state);
diff --git a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
index be49527..216581f 100644
--- a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
@@ -189,7 +189,7 @@
   if (!len)
     return;
 
-  script_.Append(decoder_->Decode(data, len));
+  source_text_.Append(decoder_->Decode(data, len));
 }
 
 void WorkerScriptLoader::DidReceiveCachedMetadata(const char* data, int size) {
@@ -200,7 +200,7 @@
 void WorkerScriptLoader::DidFinishLoading(unsigned long identifier, double) {
   need_to_cancel_ = false;
   if (!failed_ && decoder_)
-    script_.Append(decoder_->Flush());
+    source_text_.Append(decoder_->Flush());
 
   NotifyFinished();
 }
@@ -223,8 +223,8 @@
     threadable_loader_->Cancel();
 }
 
-String WorkerScriptLoader::Script() {
-  return script_.ToString();
+String WorkerScriptLoader::SourceText() {
+  return source_text_.ToString();
 }
 
 void WorkerScriptLoader::NotifyError() {
diff --git a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
index 101514e..fd44459 100644
--- a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
+++ b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
@@ -77,7 +77,7 @@
   // is in progress.
   void Cancel();
 
-  String Script();
+  String SourceText();
   const KURL& Url() const { return url_; }
   const KURL& ResponseURL() const;
   bool Failed() const { return failed_; }
@@ -140,7 +140,7 @@
   Persistent<ThreadableLoader> threadable_loader_;
   String response_encoding_;
   std::unique_ptr<TextResourceDecoder> decoder_;
-  StringBuilder script_;
+  StringBuilder source_text_;
   KURL url_;
   KURL response_url_;
 
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.idl b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.idl
index bfd4c86a..418ddb4 100644
--- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.idl
+++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.idl
@@ -9,9 +9,4 @@
     RuntimeEnabled=Worklet,
     ImmutablePrototype,
 ] interface WorkletGlobalScope {
-    // Console API
-    // https://console.spec.whatwg.org/#console-interface
-    // [Replaceable] readonly attribute Console console;
-    // Console is installed by v8 inspector when context is created
-    // and is left commented here just for documentation.
 };
diff --git a/third_party/WebKit/Source/core/xml/DocumentXMLTreeViewer.cpp b/third_party/WebKit/Source/core/xml/DocumentXMLTreeViewer.cpp
index 835366c..9b203591 100644
--- a/third_party/WebKit/Source/core/xml/DocumentXMLTreeViewer.cpp
+++ b/third_party/WebKit/Source/core/xml/DocumentXMLTreeViewer.cpp
@@ -23,7 +23,7 @@
   sources.push_back(ScriptSourceCode(script_string));
   v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate());
 
-  document.GetFrame()->Script().ExecuteScriptInIsolatedWorld(
+  document.GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(
       DOMWrapperWorld::kDocumentXMLTreeViewerWorldId, sources, nullptr);
 
   Element* element = document.GetElementById("xml-viewer-style");
diff --git a/third_party/WebKit/Source/core/xml/DocumentXSLT.cpp b/third_party/WebKit/Source/core/xml/DocumentXSLT.cpp
index 9d4239f8..e3bf7f5b 100644
--- a/third_party/WebKit/Source/core/xml/DocumentXSLT.cpp
+++ b/third_party/WebKit/Source/core/xml/DocumentXSLT.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/V8AbstractEventListener.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/Node.h"
 #include "core/dom/ProcessingInstruction.h"
 #include "core/events/Event.h"
@@ -38,7 +39,7 @@
     DCHECK_EQ(event->type(), "DOMContentLoaded");
     ScriptState::Scope scope(script_state);
 
-    Document& document = *ToDocument(script_state->GetExecutionContext());
+    Document& document = *ToDocument(ExecutionContext::From(script_state));
     DCHECK(!document.Parsing());
 
     // Processing instruction (XML documents only).
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index fc9b2d6..91265c49 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -203,7 +203,7 @@
 };
 
 XMLHttpRequest* XMLHttpRequest::Create(ScriptState* script_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   DOMWrapperWorld& world = script_state->World();
   if (!world.IsIsolatedWorld())
     return Create(context);
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.cpp b/third_party/WebKit/Source/modules/fetch/Headers.cpp
index 1e41606f..a62a640 100644
--- a/third_party/WebKit/Source/modules/fetch/Headers.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Headers.cpp
@@ -4,9 +4,9 @@
 
 #include "modules/fetch/Headers.h"
 
-#include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/V8IteratorResultValue.h"
+#include "bindings/modules/v8/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.h"
 #include "core/dom/Iterator.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/wtf/NotFound.h"
@@ -54,39 +54,27 @@
 
 }  // namespace
 
-Headers* Headers::Create() {
+Headers* Headers::Create(ExceptionState&) {
   return new Headers;
 }
 
-Headers* Headers::Create(ExceptionState&) {
-  return Create();
-}
-
-Headers* Headers::Create(const Headers* init, ExceptionState& exception_state) {
+Headers* Headers::Create(
+    const ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders& init,
+    ExceptionState& exception_state) {
   // "The Headers(|init|) constructor, when invoked, must run these steps:"
-  // "1. Let |headers| be a new Headers object."
-  Headers* headers = Create();
+  // "1. Let |headers| be a new Headers object whose guard is "none".
+  Headers* headers = Create(exception_state);
   // "2. If |init| is given, fill headers with |init|. Rethrow any exception."
-  headers->FillWith(init, exception_state);
-  // "3. Return |headers|."
-  return headers;
-}
-
-Headers* Headers::Create(const Vector<Vector<String>>& init,
-                         ExceptionState& exception_state) {
-  // The same steps as above.
-  Headers* headers = Create();
-  headers->FillWith(init, exception_state);
-  return headers;
-}
-
-Headers* Headers::Create(const Dictionary& init,
-                         ExceptionState& exception_state) {
-  // "The Headers(|init|) constructor, when invoked, must run these steps:"
-  // "1. Let |headers| be a new Headers object."
-  Headers* headers = Create();
-  // "2. If |init| is given, fill headers with |init|. Rethrow any exception."
-  headers->FillWith(init, exception_state);
+  if (init.isByteStringSequenceSequence()) {
+    headers->FillWith(init.getAsByteStringSequenceSequence(), exception_state);
+  } else if (init.isByteStringByteStringRecord()) {
+    headers->FillWith(init.getAsByteStringByteStringRecord(), exception_state);
+  } else if (init.isHeaders()) {
+    // This branch will not be necessary once http://crbug.com/690428 is fixed.
+    headers->FillWith(init.getAsHeaders(), exception_state);
+  } else {
+    NOTREACHED();
+  }
   // "3. Return |headers|."
   return headers;
 }
@@ -251,12 +239,9 @@
 
 void Headers::FillWith(const Headers* object, ExceptionState& exception_state) {
   ASSERT(header_list_->size() == 0);
-  // "To fill a Headers object (|this|) with a given object (|object|), run
-  // these steps:"
-  // "1. If |object| is a Headers object, copy its header list as
-  //     |headerListCopy| and then for each |header| in |headerListCopy|,
-  //     retaining order, append header's |name|/|header|'s value to
-  //     |headers|. Rethrow any exception."
+  // There used to be specific steps describing filling a Headers object with
+  // another Headers object, but it has since been removed because it should be
+  // handled like a sequence (http://crbug.com/690428).
   for (size_t i = 0; i < object->header_list_->List().size(); ++i) {
     append(object->header_list_->List()[i]->first,
            object->header_list_->List()[i]->second, exception_state);
@@ -268,12 +253,12 @@
 void Headers::FillWith(const Vector<Vector<String>>& object,
                        ExceptionState& exception_state) {
   ASSERT(!header_list_->size());
-  // "2. Otherwise, if |object| is a sequence, then for each |header| in
-  //     |object|, run these substeps:
-  //    1. If |header| does not contain exactly two items, throw a
-  //       TypeError.
-  //    2. Append |header|'s first item/|header|'s second item to
-  //       |headers|. Rethrow any exception."
+  // "1. If |object| is a sequence, then for each |header| in |object|, run
+  //     these substeps:
+  //     1. If |header| does not contain exactly two items, then throw a
+  //        TypeError.
+  //     2. Append |header|’s first item/|header|’s second item to |headers|.
+  //        Rethrow any exception."
   for (size_t i = 0; i < object.size(); ++i) {
     if (object[i].size() != 2) {
       exception_state.ThrowTypeError("Invalid value");
@@ -285,27 +270,12 @@
   }
 }
 
-void Headers::FillWith(const Dictionary& object,
+void Headers::FillWith(const Vector<std::pair<String, String>>& object,
                        ExceptionState& exception_state) {
   ASSERT(!header_list_->size());
-  const Vector<String>& keys = object.GetPropertyNames(exception_state);
-  if (exception_state.HadException() || !keys.size())
-    return;
 
-  // "3. Otherwise, if |object| is an open-ended dictionary, then for each
-  //    |header| in object, run these substeps:
-  //    1. Set |header|'s key to |header|'s key, converted to ByteString.
-  //       Rethrow any exception.
-  //    2. Append |header|'s key/|header|'s value to |headers|. Rethrow any
-  //       exception."
-  // FIXME: Support OpenEndedDictionary<ByteString>.
-  for (size_t i = 0; i < keys.size(); ++i) {
-    String value;
-    if (!DictionaryHelper::Get(object, keys[i], value)) {
-      exception_state.ThrowTypeError("Invalid value");
-      return;
-    }
-    append(keys[i], value, exception_state);
+  for (const auto& item : object) {
+    append(item.first, item.second, exception_state);
     if (exception_state.HadException())
       return;
   }
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.h b/third_party/WebKit/Source/modules/fetch/Headers.h
index 7cf4ce4..3c41b2d2 100644
--- a/third_party/WebKit/Source/modules/fetch/Headers.h
+++ b/third_party/WebKit/Source/modules/fetch/Headers.h
@@ -14,7 +14,7 @@
 
 namespace blink {
 
-class Dictionary;
+class ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders;
 class ExceptionState;
 
 // http://fetch.spec.whatwg.org/#headers-class
@@ -32,11 +32,10 @@
     kNoneGuard
   };
 
-  static Headers* Create();
   static Headers* Create(ExceptionState&);
-  static Headers* Create(const Headers*, ExceptionState&);
-  static Headers* Create(const Vector<Vector<String>>&, ExceptionState&);
-  static Headers* Create(const Dictionary&, ExceptionState&);
+  static Headers* Create(
+      const ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders&,
+      ExceptionState&);
 
   // Shares the FetchHeaderList. Called when creating a Request or Response.
   static Headers* Create(FetchHeaderList*);
@@ -57,7 +56,7 @@
   // These methods should only be called when size() would return 0.
   void FillWith(const Headers*, ExceptionState&);
   void FillWith(const Vector<Vector<String>>&, ExceptionState&);
-  void FillWith(const Dictionary&, ExceptionState&);
+  void FillWith(const Vector<std::pair<String, String>>&, ExceptionState&);
 
   FetchHeaderList* HeaderList() const { return header_list_; }
   DECLARE_TRACE();
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.idl b/third_party/WebKit/Source/modules/fetch/Headers.idl
index 6b2b91af..3255e90 100644
--- a/third_party/WebKit/Source/modules/fetch/Headers.idl
+++ b/third_party/WebKit/Source/modules/fetch/Headers.idl
@@ -2,14 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// https://fetch.spec.whatwg.org/#typedefdef-headersinit
+
+// The actual typedef in the spec is
+//    (sequence<sequence<ByteString>> or record<ByteString,ByteString>)
+// which also implicitly includes Headers since it is iterable.
+// See http://crbug.com/690428.
+typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString> or Headers) HeadersInit;
+
 // https://fetch.spec.whatwg.org/#headers-class
 
-typedef Dictionary OpenEndedDictionary;
 [
-    Constructor,
-    Constructor(Headers input),
-    Constructor(OpenEndedDictionary input),
-    Constructor(sequence<sequence<ByteString>> input),
+    Constructor(optional HeadersInit init),
     Exposed=(Window,Worker),
     RaisesException=Constructor,
 ] interface Headers {
diff --git a/third_party/WebKit/Source/modules/fetch/Request.cpp b/third_party/WebKit/Source/modules/fetch/Request.cpp
index 46eea38c..6751f32 100644
--- a/third_party/WebKit/Source/modules/fetch/Request.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Request.cpp
@@ -309,7 +309,7 @@
   // We don't create a copy of r's Headers object when init's headers member
   // is present.
   Headers* headers = nullptr;
-  if (!init.headers && init.headers_dictionary.IsUndefinedOrNull()) {
+  if (!init.headers) {
     headers = r->getHeaders()->Clone();
   }
   // "Empty |r|'s request's header list."
@@ -335,10 +335,7 @@
   }
   // "Fill |r|'s Headers object with |headers|. Rethrow any exceptions."
   if (init.headers) {
-    ASSERT(init.headers_dictionary.IsUndefinedOrNull());
     r->getHeaders()->FillWith(init.headers.Get(), exception_state);
-  } else if (!init.headers_dictionary.IsUndefinedOrNull()) {
-    r->getHeaders()->FillWith(init.headers_dictionary, exception_state);
   } else {
     ASSERT(headers);
     r->getHeaders()->FillWith(headers, exception_state);
diff --git a/third_party/WebKit/Source/modules/fetch/RequestInit.cpp b/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
index d23ae70..33c9fb3 100644
--- a/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
+++ b/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
@@ -11,6 +11,7 @@
 #include "bindings/core/v8/V8Blob.h"
 #include "bindings/core/v8/V8FormData.h"
 #include "bindings/core/v8/V8URLSearchParams.h"
+#include "bindings/modules/v8/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.h"
 #include "bindings/modules/v8/V8PasswordCredential.h"
 #include "core/dom/URLSearchParams.h"
 #include "core/fileapi/Blob.h"
@@ -32,18 +33,18 @@
                          ExceptionState& exception_state)
     : are_any_members_set(false) {
   are_any_members_set |= DictionaryHelper::Get(options, "method", method);
-  are_any_members_set |= DictionaryHelper::Get(options, "headers", headers);
-  if (!headers) {
-    Vector<Vector<String>> headers_vector;
-    if (DictionaryHelper::Get(options, "headers", headers_vector,
-                              exception_state)) {
-      headers = Headers::Create(headers_vector, exception_state);
-      are_any_members_set = true;
-    } else {
-      are_any_members_set |=
-          DictionaryHelper::Get(options, "headers", headers_dictionary);
-    }
-  }
+
+  // From https://github.com/whatwg/fetch/issues/479:
+  // - undefined is the same as "this member has not been passed".
+  // - {} means "the list of headers is empty", so the member has been set.
+  // - null is an invalid value for both sequences and records, but it is not
+  //   the same as undefined: a value has been set, even if invalid, and will
+  //   throw a TypeError later when it gets converted to a HeadersInit object.
+  v8::Local<v8::Value> v8_headers;
+  bool is_header_set = DictionaryHelper::Get(options, "headers", v8_headers) &&
+                       !v8_headers->IsUndefined();
+  are_any_members_set |= is_header_set;
+
   are_any_members_set |= DictionaryHelper::Get(options, "mode", mode);
   are_any_members_set |= DictionaryHelper::Get(options, "redirect", redirect);
   AtomicString referrer_string;
@@ -101,6 +102,20 @@
   }
 
   v8::Isolate* isolate = ToIsolate(context);
+
+  if (is_header_set) {
+    ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders
+        headers_init;
+    V8ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders::toImpl(
+        isolate, v8_headers, headers_init,
+        UnionTypeConversionMode::kNotNullable, exception_state);
+    if (exception_state.HadException())
+      return;
+    headers = Headers::Create(headers_init, exception_state);
+    if (exception_state.HadException())
+      return;
+  }
+
   if (is_credential_set) {
     if (V8PasswordCredential::hasInstance(v8_credential, isolate)) {
       // TODO(mkwst): According to the spec, we'd serialize this once we touch
diff --git a/third_party/WebKit/Source/modules/fetch/RequestInit.h b/third_party/WebKit/Source/modules/fetch/RequestInit.h
index cbde8fb..21b13468 100644
--- a/third_party/WebKit/Source/modules/fetch/RequestInit.h
+++ b/third_party/WebKit/Source/modules/fetch/RequestInit.h
@@ -5,8 +5,6 @@
 #ifndef RequestInit_h
 #define RequestInit_h
 
-#include <memory>
-#include "bindings/core/v8/Dictionary.h"
 #include "platform/heap/Handle.h"
 #include "platform/network/EncodedFormData.h"
 #include "platform/weborigin/Referrer.h"
@@ -16,6 +14,8 @@
 namespace blink {
 
 class BytesConsumer;
+class Dictionary;
+class ExecutionContext;
 class ExceptionState;
 class Headers;
 
@@ -28,7 +28,6 @@
 
   String method;
   Member<Headers> headers;
-  Dictionary headers_dictionary;
   String content_type;
   Member<BytesConsumer> body;
   Referrer referrer;
diff --git a/third_party/WebKit/Source/modules/fetch/Response.cpp b/third_party/WebKit/Source/modules/fetch/Response.cpp
index 57aae12b..1eae69fa 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Response.cpp
@@ -15,7 +15,7 @@
 #include "bindings/core/v8/V8FormData.h"
 #include "bindings/core/v8/V8PrivateProperty.h"
 #include "bindings/core/v8/V8URLSearchParams.h"
-#include "bindings/modules/v8/ByteStringSequenceSequenceOrDictionaryOrHeaders.h"
+#include "bindings/modules/v8/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMArrayBufferView.h"
 #include "core/dom/URLSearchParams.h"
@@ -231,8 +231,9 @@
     if (init.headers().isByteStringSequenceSequence()) {
       r->headers_->FillWith(init.headers().getAsByteStringSequenceSequence(),
                             exception_state);
-    } else if (init.headers().isDictionary()) {
-      r->headers_->FillWith(init.headers().getAsDictionary(), exception_state);
+    } else if (init.headers().isByteStringByteStringRecord()) {
+      r->headers_->FillWith(init.headers().getAsByteStringByteStringRecord(),
+                            exception_state);
     } else if (init.headers().isHeaders()) {
       r->headers_->FillWith(init.headers().getAsHeaders(), exception_state);
     }
diff --git a/third_party/WebKit/Source/modules/fetch/ResponseInit.idl b/third_party/WebKit/Source/modules/fetch/ResponseInit.idl
index 6829802..8dc8e0d5 100644
--- a/third_party/WebKit/Source/modules/fetch/ResponseInit.idl
+++ b/third_party/WebKit/Source/modules/fetch/ResponseInit.idl
@@ -4,14 +4,6 @@
 
 // https://fetch.spec.whatwg.org/#responseinit
 
-// The actual typedef in the spec is
-//    (sequence<sequence<ByteString>> or record<ByteString,ByteString>)
-// which also implicitly includes Headers since it is iterable.
-// Blink's WebIDL code does not support record<K,V> yet, so we have to make do
-// with the (Dictionary or Headers) part instead.
-// See http://crbug.com/685754.
-typedef (sequence<sequence<ByteString>> or Dictionary or Headers) HeadersInit;
-
 dictionary ResponseInit {
     unsigned short status = 200;
     ByteString statusText = "OK";
diff --git a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
index 941d836..4c31d50 100644
--- a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
+++ b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
@@ -23,7 +23,7 @@
 
 namespace {
 
-const char* g_k_default_mime_type = "video/webm";
+const char kDefaultMimeType[] = "video/webm";
 
 // Boundaries of Opus bitrate from https://www.opus-codec.org/.
 const int kSmallestPossibleOpusBitRate = 6000;
@@ -163,8 +163,7 @@
                              ExceptionState& exception_state)
     : SuspendableObject(context),
       stream_(stream),
-      mime_type_(options.hasMimeType() ? options.mimeType()
-                                       : g_k_default_mime_type),
+      mime_type_(options.hasMimeType() ? options.mimeType() : kDefaultMimeType),
       stopped_(true),
       audio_bits_per_second_(0),
       video_bits_per_second_(0),
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBufferTrackBaseSupplement.cpp b/third_party/WebKit/Source/modules/mediasource/SourceBufferTrackBaseSupplement.cpp
index 498ffaa..ee5dd77 100644
--- a/third_party/WebKit/Source/modules/mediasource/SourceBufferTrackBaseSupplement.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/SourceBufferTrackBaseSupplement.cpp
@@ -9,13 +9,13 @@
 
 namespace blink {
 
-static const char* g_k_supplement_name = "SourceBufferTrackBaseSupplement";
+static const char kSupplementName[] = "SourceBufferTrackBaseSupplement";
 
 // static
 SourceBufferTrackBaseSupplement* SourceBufferTrackBaseSupplement::FromIfExists(
     TrackBase& track) {
   return static_cast<SourceBufferTrackBaseSupplement*>(
-      Supplement<TrackBase>::From(track, g_k_supplement_name));
+      Supplement<TrackBase>::From(track, kSupplementName));
 }
 
 // static
@@ -24,7 +24,7 @@
   SourceBufferTrackBaseSupplement* supplement = FromIfExists(track);
   if (!supplement) {
     supplement = new SourceBufferTrackBaseSupplement();
-    Supplement<TrackBase>::ProvideTo(track, g_k_supplement_name, supplement);
+    Supplement<TrackBase>::ProvideTo(track, kSupplementName, supplement);
   }
   return *supplement;
 }
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
index 49770124..ceee1a9 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
@@ -16,7 +16,7 @@
 
 namespace {
 
-const char* g_k_valid_policies[] = {
+const char* const kValidPolicies[] = {
     "{\"vibrate\": []}",
     "{\"vibrate\": [\"self\"]}",
     "{\"vibrate\": [\"*\"]}",
@@ -50,7 +50,7 @@
 
 TEST_F(FeaturePolicyTest, ParseValidPolicy) {
   Vector<String> messages;
-  for (const char* policy_string : g_k_valid_policies) {
+  for (const char* policy_string : kValidPolicies) {
     messages.Clear();
     ParseFeaturePolicy(policy_string, origin_a_.Get(), &messages);
     EXPECT_EQ(0UL, messages.size());
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
index 7c5876b..ce91e40 100644
--- a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
+++ b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
@@ -59,7 +59,7 @@
 
 namespace blink {
 
-const char* g_k_color_emoji_font_mac = "Apple Color Emoji";
+const char kColorEmojiFontMac[] = "Apple Color Emoji";
 
 // static
 const AtomicString& FontCache::LegacySystemFontFamily() {
@@ -115,7 +115,7 @@
     FontFallbackPriority fallback_priority) {
   if (fallback_priority == FontFallbackPriority::kEmojiEmoji) {
     RefPtr<SimpleFontData> emoji_font =
-        GetFontData(font_description, AtomicString(g_k_color_emoji_font_mac));
+        GetFontData(font_description, AtomicString(kColorEmojiFontMac));
     if (emoji_font)
       return emoji_font;
   }
diff --git a/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp b/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp
index 0d46a9f..d41fde6 100644
--- a/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp
+++ b/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp
@@ -73,7 +73,7 @@
 // based on the proposed changes in UTR #51 for introducing
 // an Emoji script code:
 // http://www.unicode.org/reports/tr51/proposed.html#Emoji_Script
-static const char* g_k_android_color_emoji_locale = "und-Zsye";
+static const char kAndroidColorEmojiLocale[] = "und-Zsye";
 
 // This function is called on android or when we are emulating android fonts on
 // linux and the embedder has overriden the default fontManager with
@@ -101,7 +101,7 @@
   if (content_locale)
     bcp47_locales[locale_count++] = content_locale->LocaleForSkFontMgr();
   if (fallback_priority == FontFallbackPriority::kEmojiEmoji)
-    bcp47_locales[locale_count++] = g_k_android_color_emoji_locale;
+    bcp47_locales[locale_count++] = kAndroidColorEmojiLocale;
   SECURITY_DCHECK(locale_count <= kMaxLocales);
   sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(
       0, SkFontStyle(), bcp47_locales, locale_count, c));
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp b/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
index 489afd92..3930947 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp
@@ -281,8 +281,8 @@
   RunTest("Blink.DecodedImageType");
 }
 
-DecodedImageTypeHistogramTest::ParamType
-    g_k_decoded_image_type_histogram_test_params[] = {
+const DecodedImageTypeHistogramTest::ParamType
+    kDecodedImageTypeHistogramTestparams[] = {
         {"/LayoutTests/images/resources/green.jpg",
          BitmapImageMetrics::kImageJPEG},
         {"/LayoutTests/images/resources/"
@@ -300,7 +300,7 @@
 INSTANTIATE_TEST_CASE_P(
     DecodedImageTypeHistogramTest,
     DecodedImageTypeHistogramTest,
-    ::testing::ValuesIn(g_k_decoded_image_type_histogram_test_params));
+    ::testing::ValuesIn(kDecodedImageTypeHistogramTestparams));
 
 using DecodedImageOrientationHistogramTest =
     BitmapHistogramTest<ImageOrientationEnum>;
diff --git a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
index 3c788f2..d047d12 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
@@ -35,6 +35,19 @@
 
 namespace blink {
 
+static void CopyPixels(void* dst_addr,
+                       size_t dst_row_bytes,
+                       const void* src_addr,
+                       size_t src_row_bytes,
+                       const SkImageInfo& info) {
+  size_t row_bytes = info.bytesPerPixel() * info.width();
+  for (int y = 0; y < info.height(); ++y) {
+    memcpy(dst_addr, src_addr, row_bytes);
+    src_addr = static_cast<const char*>(src_addr) + src_row_bytes;
+    dst_addr = static_cast<char*>(dst_addr) + dst_row_bytes;
+  }
+}
+
 static bool CompatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) {
   if (src == dst)
     return true;
@@ -151,7 +164,7 @@
   ASSERT(bitmap.height() == scaled_size.height());
   SkAutoLockPixels bitmap_lock(bitmap);
   if (bitmap.getPixels() != pixels)
-    return bitmap.copyPixelsTo(pixels, row_bytes * info.height(), row_bytes);
+    CopyPixels(pixels, row_bytes, bitmap.getPixels(), bitmap.rowBytes(), info);
   return true;
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.cpp b/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.cpp
index 46da3a2..b6aee90 100644
--- a/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.cpp
@@ -114,7 +114,7 @@
                                     source_graphic->OperatingColorSpace());
 }
 
-static float g_k_max_mask_buffer_size =
+static const float kMaxMaskBufferSize =
     50.f * 1024.f * 1024.f / 4.f;  // 50MB / 4 bytes per pixel
 
 sk_sp<SkImageFilter> BuildBoxReflectFilter(const BoxReflection& reflection,
@@ -128,7 +128,7 @@
     const SkRect cull_rect = mask_record->cullRect();
     if (static_cast<float>(cull_rect.width()) *
             static_cast<float>(cull_rect.height()) <
-        g_k_max_mask_buffer_size) {
+        kMaxMaskBufferSize) {
       bitmap.allocPixels(
           SkImageInfo::MakeN32Premul(cull_rect.width(), cull_rect.height()));
       SkiaPaintCanvas canvas(bitmap);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
index d51dc2f..ac1cb052 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
@@ -39,7 +39,7 @@
   return task_queue_manager_->Delegate()->NowTicks();
 }
 
-void RealTimeDomain::RequestWakeupAt(base::TimeTicks now,
+void RealTimeDomain::RequestWakeUpAt(base::TimeTicks now,
                                      base::TimeTicks run_time) {
   // NOTE this is only called if the scheduled runtime is sooner than any
   // previously scheduled runtime, or there is no (outstanding) previously
@@ -47,7 +47,7 @@
   task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, this, now, run_time);
 }
 
-void RealTimeDomain::CancelWakeupAt(base::TimeTicks run_time) {
+void RealTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
   task_queue_manager_->CancelDelayedWork(this, run_time);
 }
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
index 1457204..b8a5bd5 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
@@ -29,8 +29,8 @@
  protected:
   void OnRegisterWithTaskQueueManager(
       TaskQueueManager* task_queue_manager) override;
-  void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override;
-  void CancelWakeupAt(base::TimeTicks run_time) override;
+  void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+  void CancelWakeUpAt(base::TimeTicks run_time) override;
   void AsValueIntoInternal(
       base::trace_event::TracedValue* state) const override;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
index 82bfd3c..1006ce0 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
@@ -281,9 +281,9 @@
   main_thread_only().task_queue_manager->DidQueueTask(pending_task);
   main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
 
-  // If |pending_task| is at the head of the queue, then make sure a wakeup
+  // If |pending_task| is at the head of the queue, then make sure a wake-up
   // is requested if the queue is enabled.  Note we still want to schedule a
-  // wakeup even if blocked by a fence, because we'd break throttling logic
+  // wake-up even if blocked by a fence, because we'd break throttling logic
   // otherwise.
   base::TimeTicks next_delayed_task =
       main_thread_only().delayed_incoming_queue.top().delayed_run_time;
@@ -421,7 +421,7 @@
 }
 
 base::Optional<base::TimeTicks> TaskQueueImpl::GetNextScheduledWakeUp() {
-  // Note we don't scheduled a wakeup for disabled queues.
+  // Note we don't scheduled a wake-up for disabled queues.
   if (main_thread_only().delayed_incoming_queue.empty() || !IsQueueEnabled())
     return base::nullopt;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
index ec4e74e9..e1c3a85 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
@@ -46,7 +46,7 @@
 // immediate_work_queue is swapped with immediate_incoming_queue when
 // immediate_work_queue becomes empty.
 //
-// Delayed tasks are initially posted to delayed_incoming_queue and a wakeup
+// Delayed tasks are initially posted to delayed_incoming_queue and a wake-up
 // is scheduled with the TimeDomain.  When the delay has elapsed, the TimeDomain
 // calls UpdateDelayedWorkQueue and ready delayed tasks are moved into the
 // delayed_work_queue.  Note the EnqueueOrder (used for ordering) for a delayed
@@ -193,18 +193,18 @@
   }
 
   // Enqueues any delayed tasks which should be run now on the
-  // |delayed_work_queue|. Returns the subsequent wakeup that is required, if
+  // |delayed_work_queue|. Returns the subsequent wake-up that is required, if
   // any. Must be called from the main thread.
   base::Optional<DelayedWakeUp> WakeUpForDelayedWork(LazyNow* lazy_now);
 
-  base::TimeTicks scheduled_time_domain_wakeup() const {
-    return main_thread_only().scheduled_time_domain_wakeup;
+  base::TimeTicks scheduled_time_domain_wake_up() const {
+    return main_thread_only().scheduled_time_domain_wake_up;
   }
 
-  void set_scheduled_time_domain_wakeup(
-      base::TimeTicks scheduled_time_domain_wakeup) {
-    main_thread_only().scheduled_time_domain_wakeup =
-        scheduled_time_domain_wakeup;
+  void set_scheduled_time_domain_wake_up(
+      base::TimeTicks scheduled_time_domain_wake_up) {
+    main_thread_only().scheduled_time_domain_wake_up =
+        scheduled_time_domain_wake_up;
   }
 
   HeapHandle heap_handle() const { return main_thread_only().heap_handle; }
@@ -277,7 +277,7 @@
     int voter_refcount;
     base::trace_event::BlameContext* blame_context;  // Not owned.
     EnqueueOrder current_fence;
-    base::TimeTicks scheduled_time_domain_wakeup;
+    base::TimeTicks scheduled_time_domain_wake_up;
   };
 
   ~TaskQueueImpl() override;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 2be03e3c..4265f8f3 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -166,16 +166,16 @@
   }
 }
 
-void TaskQueueManager::WakeupReadyDelayedQueues(LazyNow* lazy_now) {
+void TaskQueueManager::WakeUpReadyDelayedQueues(LazyNow* lazy_now) {
   TRACE_EVENT0(disabled_by_default_tracing_category_,
-               "TaskQueueManager::WakeupReadyDelayedQueues");
+               "TaskQueueManager::WakeUpReadyDelayedQueues");
 
   for (TimeDomain* time_domain : time_domains_) {
     if (time_domain == real_time_domain_.get()) {
-      time_domain->WakeupReadyDelayedQueues(lazy_now);
+      time_domain->WakeUpReadyDelayedQueues(lazy_now);
     } else {
       LazyNow time_domain_lazy_now = time_domain->CreateLazyNow();
-      time_domain->WakeupReadyDelayedQueues(&time_domain_lazy_now);
+      time_domain->WakeUpReadyDelayedQueues(&time_domain_lazy_now);
     }
   }
 }
@@ -240,7 +240,7 @@
     base::TimeTicks now,
     base::TimeTicks run_time) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
-  // Make sure we don't cancel another TimeDomain's wakeup.
+  // Make sure we don't cancel another TimeDomain's wake-up.
   DCHECK(!next_delayed_do_work_ ||
          next_delayed_do_work_.time_domain() == requesting_time_domain);
   {
@@ -326,7 +326,7 @@
     // avoid a lock order inversion.
     ReloadEmptyWorkQueues(queues_to_reload);
 
-    WakeupReadyDelayedQueues(&lazy_now);
+    WakeUpReadyDelayedQueues(&lazy_now);
 
     internal::WorkQueue* work_queue = nullptr;
     if (!SelectWorkQueueToService(&work_queue))
@@ -444,7 +444,7 @@
     return NextTaskDelay();
 
   // Otherwise we need to find the shortest delay, if any.  NB we don't need to
-  // call WakeupReadyDelayedQueues because it's assumed DelayTillNextTask will
+  // call WakeUpReadyDelayedQueues because it's assumed DelayTillNextTask will
   // return base::TimeDelta>() if the delayed task is due to run now.
   base::Optional<NextTaskDelay> delay_till_next_task;
   for (TimeDomain* time_domain : time_domains_) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index 999771a9..6215f40 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -252,7 +252,7 @@
 
   // Delayed Tasks with run_times <= Now() are enqueued onto the work queue and
   // reloads any empty work queues.
-  void WakeupReadyDelayedQueues(LazyNow* lazy_now);
+  void WakeUpReadyDelayedQueues(LazyNow* lazy_now);
 
   // Chooses the next work queue to service. Returns true if |out_queue|
   // indicates the queue from which the next task should be run, false to
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
index a39f1b6..a0e4922cb 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -42,14 +42,14 @@
     return base::TimeDelta();  // Makes DoWork post an immediate continuation.
   }
 
-  void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override {
+  void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override {
     // De-dupe DoWorks.
-    if (NumberOfScheduledWakeups() == 1u)
+    if (NumberOfScheduledWakeUps() == 1u)
       RequestDoWork();
   }
 
-  void CancelWakeupAt(base::TimeTicks run_time) override {
-    // We didn't post a delayed task in RequestWakeupAt so there's no need to do
+  void CancelWakeUpAt(base::TimeTicks run_time) override {
+    // We didn't post a delayed task in RequestWakeUpAt so there's no need to do
     // anything here.
   }
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index bdbf289..b797052 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -133,8 +133,8 @@
           manager_->NewTaskQueue(TaskQueue::Spec(TaskQueue::QueueType::TEST)));
   }
 
-  void WakeupReadyDelayedQueues(LazyNow lazy_now) {
-    manager_->WakeupReadyDelayedQueues(&lazy_now);
+  void WakeUpReadyDelayedQueues(LazyNow lazy_now) {
+    manager_->WakeUpReadyDelayedQueues(&lazy_now);
   }
 
   using NextTaskDelay = TaskQueueManagerForTest::NextTaskDelay;
@@ -401,7 +401,7 @@
   EXPECT_TRUE(runners_[0]->HasPendingImmediateWork());
 
   // Move the task into the |delayed_work_queue|.
-  WakeupReadyDelayedQueues(LazyNow(now_src_.get()));
+  WakeUpReadyDelayedQueues(LazyNow(now_src_.get()));
   EXPECT_FALSE(runners_[0]->delayed_work_queue()->Empty());
   EXPECT_TRUE(runners_[0]->HasPendingImmediateWork());
 
@@ -1163,12 +1163,12 @@
 
   // Move time forwards until just before the delayed task should run.
   now_src_->Advance(base::TimeDelta::FromMilliseconds(10));
-  WakeupReadyDelayedQueues(LazyNow(now_src_.get()));
+  WakeUpReadyDelayedQueues(LazyNow(now_src_.get()));
   EXPECT_FALSE(runners_[0]->HasPendingImmediateWork());
 
   // Force the delayed task onto the work queue.
   now_src_->Advance(base::TimeDelta::FromMilliseconds(2));
-  WakeupReadyDelayedQueues(LazyNow(now_src_.get()));
+  WakeUpReadyDelayedQueues(LazyNow(now_src_.get()));
   EXPECT_TRUE(runners_[0]->HasPendingImmediateWork());
 
   test_task_runner_->RunUntilIdle();
@@ -1761,7 +1761,7 @@
   voter->SetQueueEnabled(false);
 
   // When a queue has been enabled, we may get a notification if the
-  // TimeDomain's next scheduled wakeup has changed.
+  // TimeDomain's next scheduled wake-up has changed.
   EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get()));
   voter->SetQueueEnabled(true);
 
@@ -2221,16 +2221,16 @@
   task2.weak_factory_.InvalidateWeakPtrs();
   task3.weak_factory_.InvalidateWeakPtrs();
 
-  std::set<base::TimeTicks> wakeup_times;
+  std::set<base::TimeTicks> wake_up_times;
 
   RunUntilIdle(base::Bind(
-      [](std::set<base::TimeTicks>* wakeup_times,
+      [](std::set<base::TimeTicks>* wake_up_times,
          base::SimpleTestTickClock* clock) {
-        wakeup_times->insert(clock->NowTicks());
+        wake_up_times->insert(clock->NowTicks());
       },
-      &wakeup_times, now_src_.get()));
+      &wake_up_times, now_src_.get()));
 
-  EXPECT_THAT(wakeup_times,
+  EXPECT_THAT(wake_up_times,
               ElementsAre(start_time + delay1, start_time + delay4));
   EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4));
 }
@@ -2269,16 +2269,16 @@
   task2.weak_factory_.InvalidateWeakPtrs();
   task3.weak_factory_.InvalidateWeakPtrs();
 
-  std::set<base::TimeTicks> wakeup_times;
+  std::set<base::TimeTicks> wake_up_times;
 
   RunUntilIdle(base::Bind(
-      [](std::set<base::TimeTicks>* wakeup_times,
+      [](std::set<base::TimeTicks>* wake_up_times,
          base::SimpleTestTickClock* clock) {
-        wakeup_times->insert(clock->NowTicks());
+        wake_up_times->insert(clock->NowTicks());
       },
-      &wakeup_times, now_src_.get()));
+      &wake_up_times, now_src_.get()));
 
-  EXPECT_THAT(wakeup_times,
+  EXPECT_THAT(wake_up_times,
               ElementsAre(start_time + delay1, start_time + delay4));
   EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4));
 }
@@ -2314,7 +2314,7 @@
                             task4.weak_factory_.GetWeakPtr(), &run_times),
       delay4);
 
-  // Post a non-canceled task with |delay3|. So we should still get a wakeup at
+  // Post a non-canceled task with |delay3|. So we should still get a wake-up at
   // |delay3| even though we cancel |task3|.
   runners_[0]->PostDelayedTask(FROM_HERE,
                                base::Bind(&CancelableTask::RecordTimeTask,
@@ -2325,16 +2325,16 @@
   task3.weak_factory_.InvalidateWeakPtrs();
   task1.weak_factory_.InvalidateWeakPtrs();
 
-  std::set<base::TimeTicks> wakeup_times;
+  std::set<base::TimeTicks> wake_up_times;
 
   RunUntilIdle(base::Bind(
-      [](std::set<base::TimeTicks>* wakeup_times,
+      [](std::set<base::TimeTicks>* wake_up_times,
          base::SimpleTestTickClock* clock) {
-        wakeup_times->insert(clock->NowTicks());
+        wake_up_times->insert(clock->NowTicks());
       },
-      &wakeup_times, now_src_.get()));
+      &wake_up_times, now_src_.get()));
 
-  EXPECT_THAT(wakeup_times,
+  EXPECT_THAT(wake_up_times,
               ElementsAre(start_time + delay1, start_time + delay3,
                           start_time + delay4));
 
@@ -2839,7 +2839,7 @@
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay2);
   EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp());
 
-  // We don't have wakeups scheduled for disabled queues.
+  // We don't have wake-ups scheduled for disabled queues.
   std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
       runners_[0]->CreateQueueEnabledVoter();
   voter->SetQueueEnabled(false);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.h b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.h
index 1d601fb..d0d4e54 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.h
@@ -36,7 +36,7 @@
 
   void RecordIdle(base::TimeTicks now);
 
-  // TODO(altimin): Count wakeups.
+  // TODO(altimin): Count wake-ups.
 
  private:
   enum class ThreadState { ACTIVE, PAUSED };
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
index 23750b2..d581f93 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
@@ -38,23 +38,23 @@
   DCHECK(main_thread_checker_.CalledOnValidThread());
   DCHECK_EQ(queue->GetTimeDomain(), this);
   DCHECK(queue->IsQueueEnabled());
-  // We only want to store a single wakeup per queue, so we need to remove any
+  // We only want to store a single wake-up per queue, so we need to remove any
   // previously registered wake up for |queue|.
   if (queue->heap_handle().IsValid()) {
-    DCHECK_NE(queue->scheduled_time_domain_wakeup(), base::TimeTicks());
+    DCHECK_NE(queue->scheduled_time_domain_wake_up(), base::TimeTicks());
 
     // O(log n)
-    delayed_wakeup_queue_.ChangeKey(queue->heap_handle(), {wake_up, queue});
+    delayed_wake_up_queue_.ChangeKey(queue->heap_handle(), {wake_up, queue});
   } else {
     // O(log n)
-    delayed_wakeup_queue_.insert({wake_up, queue});
+    delayed_wake_up_queue_.insert({wake_up, queue});
   }
 
-  queue->set_scheduled_time_domain_wakeup(wake_up.time);
+  queue->set_scheduled_time_domain_wake_up(wake_up.time);
 
-  // If |queue| is the first wakeup then request the wakeup.
-  if (delayed_wakeup_queue_.Min().queue == queue)
-    RequestWakeupAt(now, wake_up.time);
+  // If |queue| is the first wake-up then request the wake-up.
+  if (delayed_wake_up_queue_.Min().queue == queue)
+    RequestWakeUpAt(now, wake_up.time);
 
   if (observer_)
     observer_->OnTimeDomainHasDelayedWork(queue);
@@ -69,72 +69,73 @@
   DCHECK(main_thread_checker_.CalledOnValidThread());
   DCHECK_EQ(queue->GetTimeDomain(), this);
 
-  // If no wakeup has been requested then bail out.
+  // If no wake-up has been requested then bail out.
   if (!queue->heap_handle().IsValid())
     return;
 
-  DCHECK_NE(queue->scheduled_time_domain_wakeup(), base::TimeTicks());
-  DCHECK(!delayed_wakeup_queue_.empty());
-  base::TimeTicks prev_first_wakeup = delayed_wakeup_queue_.Min().wake_up.time;
+  DCHECK_NE(queue->scheduled_time_domain_wake_up(), base::TimeTicks());
+  DCHECK(!delayed_wake_up_queue_.empty());
+  base::TimeTicks prev_first_wake_up =
+      delayed_wake_up_queue_.Min().wake_up.time;
 
   // O(log n)
-  delayed_wakeup_queue_.erase(queue->heap_handle());
+  delayed_wake_up_queue_.erase(queue->heap_handle());
 
-  if (delayed_wakeup_queue_.empty()) {
-    CancelWakeupAt(prev_first_wakeup);
-  } else if (prev_first_wakeup != delayed_wakeup_queue_.Min().wake_up.time) {
-    CancelWakeupAt(prev_first_wakeup);
-    RequestWakeupAt(Now(), delayed_wakeup_queue_.Min().wake_up.time);
+  if (delayed_wake_up_queue_.empty()) {
+    CancelWakeUpAt(prev_first_wake_up);
+  } else if (prev_first_wake_up != delayed_wake_up_queue_.Min().wake_up.time) {
+    CancelWakeUpAt(prev_first_wake_up);
+    RequestWakeUpAt(Now(), delayed_wake_up_queue_.Min().wake_up.time);
   }
 }
 
-void TimeDomain::WakeupReadyDelayedQueues(LazyNow* lazy_now) {
+void TimeDomain::WakeUpReadyDelayedQueues(LazyNow* lazy_now) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   // Wake up any queues with pending delayed work.  Note std::multimap stores
   // the elements sorted by key, so the begin() iterator points to the earliest
-  // queue to wakeup.
-  while (!delayed_wakeup_queue_.empty() &&
-         delayed_wakeup_queue_.Min().wake_up.time <= lazy_now->Now()) {
-    internal::TaskQueueImpl* queue = delayed_wakeup_queue_.Min().queue;
+  // queue to wake-up.
+  while (!delayed_wake_up_queue_.empty() &&
+         delayed_wake_up_queue_.Min().wake_up.time <= lazy_now->Now()) {
+    internal::TaskQueueImpl* queue = delayed_wake_up_queue_.Min().queue;
     base::Optional<internal::TaskQueueImpl::DelayedWakeUp> next_wake_up =
         queue->WakeUpForDelayedWork(lazy_now);
 
     if (next_wake_up) {
       // O(log n)
-      delayed_wakeup_queue_.ReplaceMin({*next_wake_up, queue});
-      queue->set_scheduled_time_domain_wakeup(next_wake_up->time);
+      delayed_wake_up_queue_.ReplaceMin({*next_wake_up, queue});
+      queue->set_scheduled_time_domain_wake_up(next_wake_up->time);
     } else {
       // O(log n)
-      delayed_wakeup_queue_.Pop();
-      DCHECK_EQ(queue->scheduled_time_domain_wakeup(), base::TimeTicks());
+      delayed_wake_up_queue_.Pop();
+      DCHECK_EQ(queue->scheduled_time_domain_wake_up(), base::TimeTicks());
     }
   }
 }
 
 bool TimeDomain::NextScheduledRunTime(base::TimeTicks* out_time) const {
   DCHECK(main_thread_checker_.CalledOnValidThread());
-  if (delayed_wakeup_queue_.empty())
+  if (delayed_wake_up_queue_.empty())
     return false;
 
-  *out_time = delayed_wakeup_queue_.Min().wake_up.time;
+  *out_time = delayed_wake_up_queue_.Min().wake_up.time;
   return true;
 }
 
 bool TimeDomain::NextScheduledTaskQueue(TaskQueue** out_task_queue) const {
   DCHECK(main_thread_checker_.CalledOnValidThread());
-  if (delayed_wakeup_queue_.empty())
+  if (delayed_wake_up_queue_.empty())
     return false;
 
-  *out_task_queue = delayed_wakeup_queue_.Min().queue;
+  *out_task_queue = delayed_wake_up_queue_.Min().queue;
   return true;
 }
 
 void TimeDomain::AsValueInto(base::trace_event::TracedValue* state) const {
   state->BeginDictionary();
   state->SetString("name", GetName());
-  state->SetInteger("registered_delay_count", delayed_wakeup_queue_.size());
-  if (!delayed_wakeup_queue_.empty()) {
-    base::TimeDelta delay = delayed_wakeup_queue_.Min().wake_up.time - Now();
+  state->SetInteger("registered_delay_count", delayed_wake_up_queue_.size());
+  if (!delayed_wake_up_queue_.empty()) {
+    base::TimeDelta delay = delayed_wake_up_queue_.Min().wake_up.time - Now();
     state->SetDouble("next_delay_ms", delay.InMillisecondsF());
   }
   AsValueIntoInternal(state);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
index 4c96597..6179d01 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
@@ -31,7 +31,7 @@
 // the wake-ups on the underlying base::MessageLoop. Various levels of de-duping
 // are employed to prevent unnecessary posting of TaskQueueManager::DoWork.
 //
-// Note the TimeDomain only knows about the first wakeup per queue, it's the
+// Note the TimeDomain only knows about the first wake-up per queue, it's the
 // responsibility of TaskQueueImpl to keep the time domain up to date if this
 // changes.
 class BLINK_PLATFORM_EXPORT TimeDomain {
@@ -92,7 +92,7 @@
 
   // Schedules a call to TaskQueueImpl::WakeUpForDelayedWork when this
   // TimeDomain reaches |delayed_run_time|.  This supersedes any previously
-  // registered wakeup for |queue|.
+  // registered wake-up for |queue|.
   void ScheduleDelayedWork(internal::TaskQueueImpl* queue,
                            internal::TaskQueueImpl::DelayedWakeUp wake_up,
                            base::TimeTicks now);
@@ -116,13 +116,13 @@
   // NOTE this is only called by ScheduleDelayedWork if the scheduled runtime
   // is sooner than any previously sheduled work or if there is no other
   // scheduled work.
-  virtual void RequestWakeupAt(base::TimeTicks now,
+  virtual void RequestWakeUpAt(base::TimeTicks now,
                                base::TimeTicks run_time) = 0;
 
   // The implementation will cancel a wake up previously requested by
-  // RequestWakeupAt.  It's expected this will be a NOP for most virtual time
+  // RequestWakeUpAt.  It's expected this will be a NOP for most virtual time
   // domains.
-  virtual void CancelWakeupAt(base::TimeTicks run_time) = 0;
+  virtual void CancelWakeUpAt(base::TimeTicks run_time) = 0;
 
   // For implementation specific tracing.
   virtual void AsValueIntoInternal(
@@ -130,10 +130,10 @@
 
   // Call TaskQueueImpl::UpdateDelayedWorkQueue for each queue where the delay
   // has elapsed.
-  void WakeupReadyDelayedQueues(LazyNow* lazy_now);
+  void WakeUpReadyDelayedQueues(LazyNow* lazy_now);
 
-  size_t NumberOfScheduledWakeups() const {
-    return delayed_wakeup_queue_.size();
+  size_t NumberOfScheduledWakeUps() const {
+    return delayed_wake_up_queue_.size();
   }
 
  private:
@@ -154,12 +154,12 @@
       DCHECK(queue->heap_handle().IsValid());
       queue->set_heap_handle(HeapHandle());
 
-      DCHECK_NE(queue->scheduled_time_domain_wakeup(), base::TimeTicks());
-      queue->set_scheduled_time_domain_wakeup(base::TimeTicks());
+      DCHECK_NE(queue->scheduled_time_domain_wake_up(), base::TimeTicks());
+      queue->set_scheduled_time_domain_wake_up(base::TimeTicks());
     }
   };
 
-  IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wakeup_queue_;
+  IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wake_up_queue_;
 
   Observer* const observer_;  // NOT OWNED.
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
index 1c47328..bc62249b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
@@ -36,7 +36,7 @@
   using TimeDomain::OnQueueHasImmediateWork;
   using TimeDomain::ScheduleDelayedWork;
   using TimeDomain::UnregisterQueue;
-  using TimeDomain::WakeupReadyDelayedQueues;
+  using TimeDomain::WakeUpReadyDelayedQueues;
 
   // TimeSource implementation:
   LazyNow CreateLazyNow() const override { return LazyNow(now_); }
@@ -53,10 +53,10 @@
   void OnRegisterWithTaskQueueManager(
       TaskQueueManager* task_queue_manager) override {}
 
-  MOCK_METHOD2(RequestWakeupAt,
+  MOCK_METHOD2(RequestWakeUpAt,
                void(base::TimeTicks now, base::TimeTicks run_time));
 
-  MOCK_METHOD1(CancelWakeupAt, void(base::TimeTicks run_time));
+  MOCK_METHOD1(CancelWakeUpAt, void(base::TimeTicks run_time));
 
   void SetNow(base::TimeTicks now) { now_ = now; }
 
@@ -92,7 +92,7 @@
 TEST_F(TimeDomainTest, ScheduleDelayedWork) {
   base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
   base::TimeTicks delayed_runtime = time_domain_->Now() + delay;
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
   base::TimeTicks now = time_domain_->Now();
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {now + delay, 0}, now);
 
@@ -105,15 +105,15 @@
   EXPECT_EQ(task_queue_.get(), next_task_queue);
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber());
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
 }
 
-TEST_F(TimeDomainTest, ScheduleDelayedWorkSupersedesPreviousWakeup) {
+TEST_F(TimeDomainTest, ScheduleDelayedWorkSupersedesPreviousWakeUp) {
   base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(100);
   base::TimeTicks delayed_runtime1 = time_domain_->Now() + delay1;
   base::TimeTicks delayed_runtime2 = time_domain_->Now() + delay2;
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime1));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime1));
   base::TimeTicks now = time_domain_->Now();
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {delayed_runtime1, 0},
                                     now);
@@ -124,9 +124,9 @@
 
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  // Now scheduler a later wakeup, which should replace the previously requested
-  // one.
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime2));
+  // Now scheduler a later wake-up, which should replace the previously
+  // requested one.
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime2));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {delayed_runtime2, 0},
                                     now);
 
@@ -134,10 +134,10 @@
   EXPECT_EQ(delayed_runtime2, next_scheduled_runtime);
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber());
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
 }
 
-TEST_F(TimeDomainTest, RequestWakeupAt_OnlyCalledForEarlierTasks) {
+TEST_F(TimeDomainTest, RequestWakeUpAt_OnlyCalledForEarlierTasks) {
   scoped_refptr<internal::TaskQueueImpl> task_queue2 = make_scoped_refptr(
       new internal::TaskQueueImpl(nullptr, time_domain_.get(),
                                   TaskQueue::Spec(TaskQueue::QueueType::TEST),
@@ -158,27 +158,27 @@
   base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(30);
   base::TimeDelta delay4 = base::TimeDelta::FromMilliseconds(1);
 
-  // RequestWakeupAt should always be called if there are no other wakeups.
+  // RequestWakeUpAt should always be called if there are no other wake-ups.
   base::TimeTicks now = time_domain_->Now();
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, now + delay1));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, now + delay1));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {now + delay1, 0}, now);
 
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  // RequestWakeupAt should not be called when scheduling later tasks.
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(0);
+  // RequestWakeUpAt should not be called when scheduling later tasks.
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(0);
   time_domain_->ScheduleDelayedWork(task_queue2.get(), {now + delay2, 0}, now);
   time_domain_->ScheduleDelayedWork(task_queue3.get(), {now + delay3, 0}, now);
 
-  // RequestWakeupAt should be called when scheduling earlier tasks.
+  // RequestWakeUpAt should be called when scheduling earlier tasks.
   Mock::VerifyAndClearExpectations(time_domain_.get());
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, now + delay4));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, now + delay4));
   time_domain_->ScheduleDelayedWork(task_queue4.get(), {now + delay4, 0}, now);
 
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _));
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(2);
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _));
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(2);
   task_queue2->UnregisterTaskQueue();
   task_queue3->UnregisterTaskQueue();
   task_queue4->UnregisterTaskQueue();
@@ -191,11 +191,11 @@
                                   "test.category", "test.category"));
 
   base::TimeTicks now = time_domain_->Now();
-  base::TimeTicks wakeup1 = now + base::TimeDelta::FromMilliseconds(10);
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, wakeup1)).Times(1);
-  time_domain_->ScheduleDelayedWork(task_queue_.get(), {wakeup1, 0}, now);
-  base::TimeTicks wakeup2 = now + base::TimeDelta::FromMilliseconds(100);
-  time_domain_->ScheduleDelayedWork(task_queue2_.get(), {wakeup2, 0}, now);
+  base::TimeTicks wake_up1 = now + base::TimeDelta::FromMilliseconds(10);
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, wake_up1)).Times(1);
+  time_domain_->ScheduleDelayedWork(task_queue_.get(), {wake_up1, 0}, now);
+  base::TimeTicks wake_up2 = now + base::TimeDelta::FromMilliseconds(100);
+  time_domain_->ScheduleDelayedWork(task_queue2_.get(), {wake_up2, 0}, now);
 
   TaskQueue* next_task_queue;
   EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
@@ -203,8 +203,8 @@
 
   testing::Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(wakeup1)).Times(1);
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, wakeup2)).Times(1);
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up1)).Times(1);
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, wake_up2)).Times(1);
 
   time_domain_->UnregisterQueue(task_queue_.get());
   task_queue_ = scoped_refptr<internal::TaskQueueImpl>();
@@ -213,17 +213,17 @@
 
   testing::Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(wakeup2)).Times(1);
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up2)).Times(1);
 
   time_domain_->UnregisterQueue(task_queue2_.get());
   EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
 }
 
-TEST_F(TimeDomainTest, WakeupReadyDelayedQueues) {
+TEST_F(TimeDomainTest, WakeUpReadyDelayedQueues) {
   base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
   base::TimeTicks now = time_domain_->Now();
   base::TimeTicks delayed_runtime = now + delay;
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {delayed_runtime, 0},
                                     now);
 
@@ -232,23 +232,23 @@
   EXPECT_EQ(delayed_runtime, next_run_time);
 
   LazyNow lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->WakeupReadyDelayedQueues(&lazy_now);
+  time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
   ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
   EXPECT_EQ(delayed_runtime, next_run_time);
 
   time_domain_->SetNow(delayed_runtime);
   lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->WakeupReadyDelayedQueues(&lazy_now);
+  time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
   ASSERT_FALSE(time_domain_->NextScheduledRunTime(&next_run_time));
 }
 
-TEST_F(TimeDomainTest, WakeupReadyDelayedQueuesWithIdenticalRuntimes) {
+TEST_F(TimeDomainTest, WakeUpReadyDelayedQueuesWithIdenticalRuntimes) {
   int sequence_num = 0;
   base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
   base::TimeTicks now = time_domain_->Now();
   base::TimeTicks delayed_runtime = now + delay;
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime));
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(delayed_runtime));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(delayed_runtime));
 
   scoped_refptr<internal::TaskQueueImpl> task_queue2 = make_scoped_refptr(
       new internal::TaskQueueImpl(nullptr, time_domain_.get(),
@@ -261,7 +261,7 @@
                                     {delayed_runtime, ++sequence_num}, now);
 
   LazyNow lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->WakeupReadyDelayedQueues(&lazy_now);
+  time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
 
   // The second task queue should wake up first since it has a lower sequence
   // number.
@@ -276,14 +276,14 @@
   base::TimeTicks now = time_domain_->Now();
   base::TimeTicks run_time = now + base::TimeDelta::FromMilliseconds(20);
 
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {run_time, 0}, now);
 
   TaskQueue* next_task_queue;
   EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
   EXPECT_EQ(task_queue_.get(), next_task_queue);
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(run_time));
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(run_time));
   time_domain_->CancelDelayedWork(task_queue_.get());
   EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
 }
@@ -297,11 +297,11 @@
   base::TimeTicks now = time_domain_->Now();
   base::TimeTicks run_time1 = now + base::TimeDelta::FromMilliseconds(20);
   base::TimeTicks run_time2 = now + base::TimeDelta::FromMilliseconds(40);
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time1));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time1));
   time_domain_->ScheduleDelayedWork(task_queue_.get(), {run_time1, 0}, now);
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(0);
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(0);
   time_domain_->ScheduleDelayedWork(task_queue2.get(), {run_time2, 0}, now);
   Mock::VerifyAndClearExpectations(time_domain_.get());
 
@@ -313,8 +313,8 @@
   ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
   EXPECT_EQ(run_time1, next_run_time);
 
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(run_time1));
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time2));
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(run_time1));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time2));
   time_domain_->CancelDelayedWork(task_queue_.get());
   EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
   EXPECT_EQ(task_queue2.get(), next_task_queue);
@@ -323,8 +323,8 @@
   EXPECT_EQ(run_time2, next_run_time);
 
   Mock::VerifyAndClearExpectations(time_domain_.get());
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(AnyNumber());
-  EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber());
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(AnyNumber());
+  EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
 
   // Tidy up.
   task_queue2->UnregisterTaskQueue();
@@ -357,7 +357,7 @@
 
 TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) {
   EXPECT_CALL(*observer_, OnTimeDomainHasDelayedWork(task_queue_.get()));
-  EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _));
+  EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _));
   base::TimeTicks now = time_domain_->Now();
   time_domain_->ScheduleDelayedWork(
       task_queue_.get(), {now + base::TimeDelta::FromMilliseconds(10), 0}, now);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
index 56c7d37..2da1c7c 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
@@ -34,15 +34,15 @@
   return now_;
 }
 
-void VirtualTimeDomain::RequestWakeupAt(base::TimeTicks now,
+void VirtualTimeDomain::RequestWakeUpAt(base::TimeTicks now,
                                         base::TimeTicks run_time) {
   // We don't need to do anything here because the caller of AdvanceTo is
   // responsible for calling TaskQueueManager::MaybeScheduleImmediateWork if
   // needed.
 }
 
-void VirtualTimeDomain::CancelWakeupAt(base::TimeTicks run_time) {
-  // We ignore this because RequestWakeupAt is a NOP.
+void VirtualTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+  // We ignore this because RequestWakeUpAt is a NOP.
 }
 
 base::Optional<base::TimeDelta> VirtualTimeDomain::DelayTillNextTask(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
index a1503f0..67aaa4fb 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
@@ -32,8 +32,8 @@
  protected:
   void OnRegisterWithTaskQueueManager(
       TaskQueueManager* task_queue_manager) override;
-  void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override;
-  void CancelWakeupAt(base::TimeTicks run_time) override;
+  void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+  void CancelWakeUpAt(base::TimeTicks run_time) override;
   void AsValueIntoInternal(
       base::trace_event::TracedValue* state) const override;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
index d958f3de..46eac1b 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
@@ -110,7 +110,7 @@
       return IdlePeriodState::IN_LONG_IDLE_PERIOD;
     }
   } else {
-    // If we can't start the idle period yet then try again after wakeup.
+    // If we can't start the idle period yet then try again after wake-up.
     *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
         kRetryEnableLongIdlePeriodDelayMillis);
     return IdlePeriodState::NOT_IN_IDLE_PERIOD;
diff --git a/third_party/WebKit/Source/platform/scheduler/child/webthread_base.cc b/third_party/WebKit/Source/platform/scheduler/child/webthread_base.cc
index f5eb1ea..d8f19ee3 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/webthread_base.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/webthread_base.cc
@@ -12,8 +12,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/pending_task.h"
 #include "base/threading/platform_thread.h"
-#include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
+#include "platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
 #include "public/platform/WebTraceLocation.h"
+#include "public/platform/scheduler/child/compositor_worker_scheduler.h"
+#include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
 
 namespace blink {
 namespace scheduler {
@@ -100,5 +102,38 @@
   return GetTaskRunner()->BelongsToCurrentThread();
 }
 
+namespace {
+
+class WebThreadForCompositor : public WebThreadImplForWorkerScheduler {
+ public:
+  explicit WebThreadForCompositor(base::Thread::Options options)
+      : WebThreadImplForWorkerScheduler("Compositor", options) {
+    Init();
+  }
+  ~WebThreadForCompositor() override {}
+
+ private:
+  // WebThreadImplForWorkerScheduler:
+  std::unique_ptr<blink::scheduler::WorkerScheduler> CreateWorkerScheduler()
+      override {
+    return base::MakeUnique<CompositorWorkerScheduler>(GetThread());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WebThreadForCompositor);
+};
+
+}  // namespace
+
+std::unique_ptr<WebThreadBase> WebThreadBase::CreateWorkerThread(
+    const char* name,
+    base::Thread::Options options) {
+  return base::MakeUnique<WebThreadImplForWorkerScheduler>(name, options);
+}
+
+std::unique_ptr<WebThreadBase> WebThreadBase::CreateCompositorThread(
+    base::Thread::Options options) {
+  return base::MakeUnique<WebThreadForCompositor>(options);
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
index 03033a4..7ecd037 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
 
 #include "base/bind.h"
 #include "base/location.h"
diff --git a/third_party/WebKit/public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
similarity index 98%
rename from third_party/WebKit/public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
rename to third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
index f33c0f31..21107a5 100644
--- a/third_party/WebKit/public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
@@ -35,8 +35,6 @@
                                   base::Thread::Options options);
   ~WebThreadImplForWorkerScheduler() override;
 
-  void Init();
-
   // WebThread implementation.
   WebScheduler* Scheduler() const override;
   PlatformThreadId ThreadId() const override;
@@ -45,6 +43,7 @@
   // WebThreadBase implementation.
   base::SingleThreadTaskRunner* GetTaskRunner() const override;
   scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+  void Init() override;
 
   // base::MessageLoop::DestructionObserver implementation.
   void WillDestroyCurrentMessageLoop() override;
diff --git a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
index 3ab108e..1d271154 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "public/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
 
 #include "base/macros.h"
 #include "base/synchronization/waitable_event.h"
@@ -150,7 +150,7 @@
   }));
 
   thread_->PostIdleTask(BLINK_FROM_HERE, task.release());
-  // We need to post a wakeup task or idle work will never happen.
+  // We need to post a wake-up task or idle work will never happen.
   thread_->GetWebTaskRunner()->PostDelayedTask(BLINK_FROM_HERE,
                                                WTF::Bind([] {}), 50ll);
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
index 57a44ed..4da9eb5 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
@@ -24,16 +24,16 @@
   return base::TimeDelta();  // Makes DoWork post an immediate continuation.
 }
 
-void AutoAdvancingVirtualTimeDomain::RequestWakeupAt(base::TimeTicks now,
+void AutoAdvancingVirtualTimeDomain::RequestWakeUpAt(base::TimeTicks now,
                                                      base::TimeTicks run_time) {
   // Avoid posting pointless DoWorks.  I.e. if the time domain has more then one
   // scheduled wake up then we don't need to do anything.
-  if (can_advance_virtual_time_ && NumberOfScheduledWakeups() == 1u)
+  if (can_advance_virtual_time_ && NumberOfScheduledWakeUps() == 1u)
     RequestDoWork();
 }
 
-void AutoAdvancingVirtualTimeDomain::CancelWakeupAt(base::TimeTicks run_time) {
-  // We ignore this because RequestWakeupAt doesn't post a delayed task.
+void AutoAdvancingVirtualTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+  // We ignore this because RequestWakeUpAt doesn't post a delayed task.
 }
 
 void AutoAdvancingVirtualTimeDomain::SetCanAdvanceVirtualTime(
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
index 9cdd5d8..1002787c 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
@@ -28,8 +28,8 @@
 
   // TimeDomain implementation:
   base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
-  void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override;
-  void CancelWakeupAt(base::TimeTicks run_time) override;
+  void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+  void CancelWakeUpAt(base::TimeTicks run_time) override;
   const char* GetName() const override;
 
   // Controls whether or not virtual time is allowed to advance, when the
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool.h b/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool.h
index de4f8ed77..9bcb72a 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool.h
@@ -116,11 +116,11 @@
   // to unblock and run tasks again. When unblocked, it still can run tasks
   // when budget is positive but less than this level until being blocked
   // until being blocked when budget reaches zero.
-  // This is needed for integration with WakeupBudgetPool to prevent a situation
-  // when wakeup happened but time budget pool allows only one task to run at
+  // This is needed for integration with WakeUpBudgetPool to prevent a situation
+  // when wake-up happened but time budget pool allows only one task to run at
   // the moment.
-  // It is recommended to use the same value for this and WakeupBudgetPool's
-  // wakeup window length.
+  // It is recommended to use the same value for this and WakeUpBudgetPool's
+  // wake-up window length.
   // NOTE: This does not have an immediate effect and does not call
   // BudgetPoolController::UnblockQueue.
   void SetMinBudgetLevelToRun(base::TimeTicks now,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
index d20b37b..2c63547 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.h
@@ -46,7 +46,7 @@
   // Deletes the budget pool.
   virtual void UnregisterBudgetPool(BudgetPool* budget_pool) = 0;
 
-  // Insert a fence to prevent tasks from running and schedule a wakeup at
+  // Insert a fence to prevent tasks from running and schedule a wake-up at
   // an appropriate time.
   virtual void BlockQueue(base::TimeTicks now, TaskQueue* queue) = 0;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
index 348d034..e5179dd 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
@@ -17,14 +17,14 @@
   return "ThrottledTimeDomain";
 }
 
-void ThrottledTimeDomain::RequestWakeupAt(base::TimeTicks now,
+void ThrottledTimeDomain::RequestWakeUpAt(base::TimeTicks now,
                                           base::TimeTicks run_time) {
-  // We assume the owner (i.e. TaskQueueThrottler) will manage wakeups on our
+  // We assume the owner (i.e. TaskQueueThrottler) will manage wake-ups on our
   // behalf.
 }
 
-void ThrottledTimeDomain::CancelWakeupAt(base::TimeTicks run_time) {
-  // We ignore this because RequestWakeupAt is a NOP.
+void ThrottledTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+  // We ignore this because RequestWakeUpAt is a NOP.
 }
 
 base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask(
@@ -37,7 +37,7 @@
   if (now >= next_run_time)
     return base::TimeDelta();  // Makes DoWork post an immediate continuation.
 
-  // We assume the owner (i.e. TaskQueueThrottler) will manage wakeups on our
+  // We assume the owner (i.e. TaskQueueThrottler) will manage wake-ups on our
   // behalf.
   return base::nullopt;
 }
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
index 4faaf5b..74a654f0 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
@@ -12,7 +12,7 @@
 namespace scheduler {
 
 // A time domain for throttled tasks. behaves like an RealTimeDomain except it
-// relies on the owner (TaskQueueThrottler) to schedule wakeups.
+// relies on the owner (TaskQueueThrottler) to schedule wake-ups.
 class BLINK_PLATFORM_EXPORT ThrottledTimeDomain : public RealTimeDomain {
  public:
   ThrottledTimeDomain(TimeDomain::Observer* observer,
@@ -21,11 +21,11 @@
 
   // TimeDomain implementation:
   const char* GetName() const override;
-  void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override;
-  void CancelWakeupAt(base::TimeTicks run_time) override;
+  void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+  void CancelWakeUpAt(base::TimeTicks run_time) override;
   base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
 
-  using TimeDomain::WakeupReadyDelayedQueues;
+  using TimeDomain::WakeUpReadyDelayedQueues;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ThrottledTimeDomain);
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
index e1fee400..0faefb3 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
@@ -67,5 +67,7 @@
   scheduler_->RemoveTaskTimeObserver(task_time_observer);
 }
 
+void WebThreadImplForRendererScheduler::Init() {}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
index 4c06b47d..940f565 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
@@ -32,6 +32,7 @@
   // WebThreadBase implementation.
   base::SingleThreadTaskRunner* GetTaskRunner() const override;
   SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+  void Init() override;
 
  private:
   void AddTaskObserverInternal(
diff --git a/third_party/WebKit/Source/platform/scheduler/utility/webthread_impl_for_utility_thread.cc b/third_party/WebKit/Source/platform/scheduler/utility/webthread_impl_for_utility_thread.cc
index 8321d97..b1a64834 100644
--- a/third_party/WebKit/Source/platform/scheduler/utility/webthread_impl_for_utility_thread.cc
+++ b/third_party/WebKit/Source/platform/scheduler/utility/webthread_impl_for_utility_thread.cc
@@ -35,5 +35,7 @@
   return nullptr;
 }
 
+void WebThreadImplForUtilityThread::Init() {}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/wtf/text/StringViewTest.cpp b/third_party/WebKit/Source/platform/wtf/text/StringViewTest.cpp
index af47935..599c2f26 100644
--- a/third_party/WebKit/Source/platform/wtf/text/StringViewTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/StringViewTest.cpp
@@ -11,12 +11,12 @@
 
 namespace WTF {
 
-const char* g_k_chars = "12345";
-const LChar* g_k_chars8 = reinterpret_cast<const LChar*>(g_k_chars);
-const UChar* g_k_chars16 = reinterpret_cast<const UChar*>(u"12345");
+const char kChars[] = "12345";
+const LChar* const kChars8 = reinterpret_cast<const LChar*>(kChars);
+const UChar* const kChars16 = reinterpret_cast<const UChar*>(u"12345");
 
 TEST(StringViewTest, ConstructionStringImpl8) {
-  RefPtr<StringImpl> impl8_bit = StringImpl::Create(g_k_chars8, 5);
+  RefPtr<StringImpl> impl8_bit = StringImpl::Create(kChars8, 5);
 
   // StringView(StringImpl*);
   ASSERT_TRUE(StringView(impl8_bit.Get()).Is8Bit());
@@ -24,7 +24,7 @@
   EXPECT_EQ(impl8_bit->Characters8(),
             StringView(impl8_bit.Get()).Characters8());
   EXPECT_EQ(impl8_bit->length(), StringView(impl8_bit.Get()).length());
-  EXPECT_STREQ(g_k_chars, StringView(impl8_bit.Get()).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(impl8_bit.Get()).ToString().Utf8().Data());
 
   // StringView(StringImpl*, unsigned offset);
   ASSERT_TRUE(StringView(impl8_bit.Get(), 2).Is8Bit());
@@ -46,7 +46,7 @@
 }
 
 TEST(StringViewTest, ConstructionStringImpl16) {
-  RefPtr<StringImpl> impl16_bit = StringImpl::Create(g_k_chars16, 5);
+  RefPtr<StringImpl> impl16_bit = StringImpl::Create(kChars16, 5);
 
   // StringView(StringImpl*);
   ASSERT_FALSE(StringView(impl16_bit.Get()).Is8Bit());
@@ -54,8 +54,7 @@
   EXPECT_EQ(impl16_bit->Characters16(),
             StringView(impl16_bit.Get()).Characters16());
   EXPECT_EQ(impl16_bit->length(), StringView(impl16_bit.Get()).length());
-  EXPECT_STREQ(g_k_chars,
-               StringView(impl16_bit.Get()).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(impl16_bit.Get()).ToString().Utf8().Data());
 
   // StringView(StringImpl*, unsigned offset);
   ASSERT_FALSE(StringView(impl16_bit.Get(), 2).Is8Bit());
@@ -78,14 +77,14 @@
 }
 
 TEST(StringViewTest, ConstructionStringImplRef8) {
-  RefPtr<StringImpl> impl8_bit = StringImpl::Create(g_k_chars8, 5);
+  RefPtr<StringImpl> impl8_bit = StringImpl::Create(kChars8, 5);
 
   // StringView(StringImpl&);
   ASSERT_TRUE(StringView(*impl8_bit).Is8Bit());
   EXPECT_FALSE(StringView(*impl8_bit).IsNull());
   EXPECT_EQ(impl8_bit->Characters8(), StringView(*impl8_bit).Characters8());
   EXPECT_EQ(impl8_bit->length(), StringView(*impl8_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(*impl8_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(*impl8_bit).ToString().Utf8().Data());
 
   // StringView(StringImpl&, unsigned offset);
   ASSERT_TRUE(StringView(*impl8_bit, 2).Is8Bit());
@@ -107,14 +106,14 @@
 }
 
 TEST(StringViewTest, ConstructionStringImplRef16) {
-  RefPtr<StringImpl> impl16_bit = StringImpl::Create(g_k_chars16, 5);
+  RefPtr<StringImpl> impl16_bit = StringImpl::Create(kChars16, 5);
 
   // StringView(StringImpl&);
   ASSERT_FALSE(StringView(*impl16_bit).Is8Bit());
   EXPECT_FALSE(StringView(*impl16_bit).IsNull());
   EXPECT_EQ(impl16_bit->Characters16(), StringView(*impl16_bit).Characters16());
   EXPECT_EQ(impl16_bit->length(), StringView(*impl16_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(*impl16_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(*impl16_bit).ToString().Utf8().Data());
 
   // StringView(StringImpl&, unsigned offset);
   ASSERT_FALSE(StringView(*impl16_bit, 2).Is8Bit());
@@ -136,14 +135,14 @@
 }
 
 TEST(StringViewTest, ConstructionString8) {
-  String string8_bit = String(StringImpl::Create(g_k_chars8, 5));
+  String string8_bit = String(StringImpl::Create(kChars8, 5));
 
   // StringView(const String&);
   ASSERT_TRUE(StringView(string8_bit).Is8Bit());
   EXPECT_FALSE(StringView(string8_bit).IsNull());
   EXPECT_EQ(string8_bit.Characters8(), StringView(string8_bit).Characters8());
   EXPECT_EQ(string8_bit.length(), StringView(string8_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(string8_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(string8_bit).ToString().Utf8().Data());
 
   // StringView(const String&, unsigned offset);
   ASSERT_TRUE(StringView(string8_bit, 2).Is8Bit());
@@ -165,7 +164,7 @@
 }
 
 TEST(StringViewTest, ConstructionString16) {
-  String string16_bit = String(StringImpl::Create(g_k_chars16, 5));
+  String string16_bit = String(StringImpl::Create(kChars16, 5));
 
   // StringView(const String&);
   ASSERT_FALSE(StringView(string16_bit).Is8Bit());
@@ -173,7 +172,7 @@
   EXPECT_EQ(string16_bit.Characters16(),
             StringView(string16_bit).Characters16());
   EXPECT_EQ(string16_bit.length(), StringView(string16_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(string16_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(string16_bit).ToString().Utf8().Data());
 
   // StringView(const String&, unsigned offset);
   ASSERT_FALSE(StringView(string16_bit, 2).Is8Bit());
@@ -195,14 +194,14 @@
 }
 
 TEST(StringViewTest, ConstructionAtomicString8) {
-  AtomicString atom8_bit = AtomicString(StringImpl::Create(g_k_chars8, 5));
+  AtomicString atom8_bit = AtomicString(StringImpl::Create(kChars8, 5));
 
   // StringView(const AtomicString&);
   ASSERT_TRUE(StringView(atom8_bit).Is8Bit());
   EXPECT_FALSE(StringView(atom8_bit).IsNull());
   EXPECT_EQ(atom8_bit.Characters8(), StringView(atom8_bit).Characters8());
   EXPECT_EQ(atom8_bit.length(), StringView(atom8_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(atom8_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(atom8_bit).ToString().Utf8().Data());
 
   // StringView(const AtomicString&, unsigned offset);
   ASSERT_TRUE(StringView(atom8_bit, 2).Is8Bit());
@@ -224,14 +223,14 @@
 }
 
 TEST(StringViewTest, ConstructionAtomicString16) {
-  AtomicString atom16_bit = AtomicString(StringImpl::Create(g_k_chars16, 5));
+  AtomicString atom16_bit = AtomicString(StringImpl::Create(kChars16, 5));
 
   // StringView(const AtomicString&);
   ASSERT_FALSE(StringView(atom16_bit).Is8Bit());
   EXPECT_FALSE(StringView(atom16_bit).IsNull());
   EXPECT_EQ(atom16_bit.Characters16(), StringView(atom16_bit).Characters16());
   EXPECT_EQ(atom16_bit.length(), StringView(atom16_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(atom16_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(atom16_bit).ToString().Utf8().Data());
 
   // StringView(const AtomicString&, unsigned offset);
   ASSERT_FALSE(StringView(atom16_bit, 2).Is8Bit());
@@ -253,14 +252,14 @@
 }
 
 TEST(StringViewTest, ConstructionStringView8) {
-  StringView view8_bit = StringView(g_k_chars8, 5);
+  StringView view8_bit = StringView(kChars8, 5);
 
   // StringView(StringView&);
   ASSERT_TRUE(StringView(view8_bit).Is8Bit());
   EXPECT_FALSE(StringView(view8_bit).IsNull());
   EXPECT_EQ(view8_bit.Characters8(), StringView(view8_bit).Characters8());
   EXPECT_EQ(view8_bit.length(), StringView(view8_bit).length());
-  EXPECT_STREQ(g_k_chars, StringView(view8_bit).ToString().Utf8().Data());
+  EXPECT_STREQ(kChars, StringView(view8_bit).ToString().Utf8().Data());
 
   // StringView(const StringView&, unsigned offset);
   ASSERT_TRUE(StringView(view8_bit, 2).Is8Bit());
@@ -282,14 +281,14 @@
 }
 
 TEST(StringViewTest, ConstructionStringView16) {
-  StringView view16_bit = StringView(g_k_chars16, 5);
+  StringView view16_bit = StringView(kChars16, 5);
 
   // StringView(StringView&);
   ASSERT_FALSE(StringView(view16_bit).Is8Bit());
   EXPECT_FALSE(StringView(view16_bit).IsNull());
   EXPECT_EQ(view16_bit.Characters16(), StringView(view16_bit).Characters16());
   EXPECT_EQ(view16_bit.length(), StringView(view16_bit).length());
-  EXPECT_EQ(g_k_chars, StringView(view16_bit).ToString());
+  EXPECT_EQ(kChars, StringView(view16_bit).ToString());
 
   // StringView(const StringView&, unsigned offset);
   ASSERT_FALSE(StringView(view16_bit, 2).Is8Bit());
@@ -312,64 +311,63 @@
 
 TEST(StringViewTest, ConstructionLiteral8) {
   // StringView(const LChar* chars);
-  ASSERT_TRUE(StringView(g_k_chars8).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars8).IsNull());
-  EXPECT_EQ(g_k_chars8, StringView(g_k_chars8).Characters8());
-  EXPECT_EQ(5u, StringView(g_k_chars8).length());
-  EXPECT_STREQ(g_k_chars, StringView(g_k_chars8).ToString().Utf8().Data());
+  ASSERT_TRUE(StringView(kChars8).Is8Bit());
+  EXPECT_FALSE(StringView(kChars8).IsNull());
+  EXPECT_EQ(kChars8, StringView(kChars8).Characters8());
+  EXPECT_EQ(5u, StringView(kChars8).length());
+  EXPECT_STREQ(kChars, StringView(kChars8).ToString().Utf8().Data());
 
   // StringView(const char* chars);
-  ASSERT_TRUE(StringView(g_k_chars).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars).IsNull());
-  EXPECT_EQ(g_k_chars8, StringView(g_k_chars).Characters8());
-  EXPECT_EQ(5u, StringView(g_k_chars).length());
-  EXPECT_STREQ(g_k_chars, StringView(g_k_chars).ToString().Utf8().Data());
+  ASSERT_TRUE(StringView(kChars).Is8Bit());
+  EXPECT_FALSE(StringView(kChars).IsNull());
+  EXPECT_EQ(kChars8, StringView(kChars).Characters8());
+  EXPECT_EQ(5u, StringView(kChars).length());
+  EXPECT_STREQ(kChars, StringView(kChars).ToString().Utf8().Data());
 
   // StringView(const LChar* chars, unsigned length);
-  ASSERT_TRUE(StringView(g_k_chars8, 2).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars8, 2).IsNull());
-  EXPECT_EQ(2u, StringView(g_k_chars8, 2).length());
-  EXPECT_EQ(StringView("12"), StringView(g_k_chars8, 2));
-  EXPECT_STREQ("12", StringView(g_k_chars8, 2).ToString().Utf8().Data());
+  ASSERT_TRUE(StringView(kChars8, 2).Is8Bit());
+  EXPECT_FALSE(StringView(kChars8, 2).IsNull());
+  EXPECT_EQ(2u, StringView(kChars8, 2).length());
+  EXPECT_EQ(StringView("12"), StringView(kChars8, 2));
+  EXPECT_STREQ("12", StringView(kChars8, 2).ToString().Utf8().Data());
 
   // StringView(const char* chars, unsigned length);
-  ASSERT_TRUE(StringView(g_k_chars, 2).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars, 2).IsNull());
-  EXPECT_EQ(2u, StringView(g_k_chars, 2).length());
-  EXPECT_EQ(StringView("12"), StringView(g_k_chars, 2));
-  EXPECT_STREQ("12", StringView(g_k_chars, 2).ToString().Utf8().Data());
+  ASSERT_TRUE(StringView(kChars, 2).Is8Bit());
+  EXPECT_FALSE(StringView(kChars, 2).IsNull());
+  EXPECT_EQ(2u, StringView(kChars, 2).length());
+  EXPECT_EQ(StringView("12"), StringView(kChars, 2));
+  EXPECT_STREQ("12", StringView(kChars, 2).ToString().Utf8().Data());
 }
 
 TEST(StringViewTest, ConstructionLiteral16) {
   // StringView(const UChar* chars);
-  ASSERT_FALSE(StringView(g_k_chars16).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars16).IsNull());
-  EXPECT_EQ(g_k_chars16, StringView(g_k_chars16).Characters16());
-  EXPECT_EQ(5u, StringView(g_k_chars16).length());
-  EXPECT_EQ(String(g_k_chars16),
-            StringView(g_k_chars16).ToString().Utf8().Data());
+  ASSERT_FALSE(StringView(kChars16).Is8Bit());
+  EXPECT_FALSE(StringView(kChars16).IsNull());
+  EXPECT_EQ(kChars16, StringView(kChars16).Characters16());
+  EXPECT_EQ(5u, StringView(kChars16).length());
+  EXPECT_EQ(String(kChars16), StringView(kChars16).ToString().Utf8().Data());
 
   // StringView(const UChar* chars, unsigned length);
-  ASSERT_FALSE(StringView(g_k_chars16, 2).Is8Bit());
-  EXPECT_FALSE(StringView(g_k_chars16, 2).IsNull());
-  EXPECT_EQ(g_k_chars16, StringView(g_k_chars16, 2).Characters16());
-  EXPECT_EQ(StringView("12"), StringView(g_k_chars16, 2));
+  ASSERT_FALSE(StringView(kChars16, 2).Is8Bit());
+  EXPECT_FALSE(StringView(kChars16, 2).IsNull());
+  EXPECT_EQ(kChars16, StringView(kChars16, 2).Characters16());
+  EXPECT_EQ(StringView("12"), StringView(kChars16, 2));
   EXPECT_EQ(StringView(reinterpret_cast<const UChar*>(u"12")),
-            StringView(g_k_chars16, 2));
-  EXPECT_EQ(2u, StringView(g_k_chars16, 2).length());
-  EXPECT_EQ(String("12"), StringView(g_k_chars16, 2).ToString());
+            StringView(kChars16, 2));
+  EXPECT_EQ(2u, StringView(kChars16, 2).length());
+  EXPECT_EQ(String("12"), StringView(kChars16, 2).ToString());
 }
 
 TEST(StringViewTest, IsEmpty) {
-  EXPECT_FALSE(StringView(g_k_chars).IsEmpty());
-  EXPECT_TRUE(StringView(g_k_chars, 0).IsEmpty());
-  EXPECT_FALSE(StringView(String(g_k_chars)).IsEmpty());
-  EXPECT_TRUE(StringView(String(g_k_chars), 5).IsEmpty());
-  EXPECT_TRUE(StringView(String(g_k_chars), 4, 0).IsEmpty());
+  EXPECT_FALSE(StringView(kChars).IsEmpty());
+  EXPECT_TRUE(StringView(kChars, 0).IsEmpty());
+  EXPECT_FALSE(StringView(String(kChars)).IsEmpty());
+  EXPECT_TRUE(StringView(String(kChars), 5).IsEmpty());
+  EXPECT_TRUE(StringView(String(kChars), 4, 0).IsEmpty());
   EXPECT_TRUE(StringView().IsEmpty());
   EXPECT_TRUE(StringView("").IsEmpty());
   EXPECT_TRUE(StringView(reinterpret_cast<const UChar*>(u"")).IsEmpty());
-  EXPECT_FALSE(StringView(g_k_chars16).IsEmpty());
+  EXPECT_FALSE(StringView(kChars16).IsEmpty());
 }
 
 TEST(StringViewTest, ToString) {
@@ -381,14 +379,14 @@
 TEST(StringViewTest, ToAtomicString) {
   EXPECT_EQ(g_null_atom.Impl(), StringView().ToAtomicString());
   EXPECT_EQ(g_empty_atom.Impl(), StringView("").ToAtomicString());
-  EXPECT_EQ(AtomicString("12"), StringView(g_k_chars8, 2).ToAtomicString());
+  EXPECT_EQ(AtomicString("12"), StringView(kChars8, 2).ToAtomicString());
   // AtomicString will convert to 8bit if possible when creating the string.
   EXPECT_EQ(AtomicString("12").Impl(),
-            StringView(g_k_chars16, 2).ToAtomicString().Impl());
+            StringView(kChars16, 2).ToAtomicString().Impl());
 }
 
 TEST(StringViewTest, ToStringImplSharing) {
-  String string(g_k_chars);
+  String string(kChars);
   EXPECT_EQ(string.Impl(), StringView(string).SharedImpl());
   EXPECT_EQ(string.Impl(), StringView(string).ToString().Impl());
   EXPECT_EQ(string.Impl(), StringView(string).ToAtomicString().Impl());
@@ -399,7 +397,7 @@
   EXPECT_TRUE(StringView(String()).IsNull());
   EXPECT_TRUE(StringView(AtomicString()).IsNull());
   EXPECT_TRUE(StringView(static_cast<const char*>(nullptr)).IsNull());
-  StringView view(g_k_chars);
+  StringView view(kChars);
   EXPECT_FALSE(view.IsNull());
   view.Clear();
   EXPECT_TRUE(view.IsNull());
@@ -416,10 +414,10 @@
 }
 
 TEST(StringViewTest, IndexAccess) {
-  StringView view8(g_k_chars8);
+  StringView view8(kChars8);
   EXPECT_EQ('1', view8[0]);
   EXPECT_EQ('3', view8[2]);
-  StringView view16(g_k_chars16);
+  StringView view16(kChars16);
   EXPECT_EQ('1', view16[0]);
   EXPECT_EQ('3', view16[2]);
 }
diff --git a/third_party/WebKit/Source/web/InspectorOverlay.cpp b/third_party/WebKit/Source/web/InspectorOverlay.cpp
index dfa2d0c8..5da6fcf 100644
--- a/third_party/WebKit/Source/web/InspectorOverlay.cpp
+++ b/third_party/WebKit/Source/web/InspectorOverlay.cpp
@@ -624,7 +624,7 @@
   command->pushValue(protocol::StringValue::create(method));
   command->pushValue(protocol::StringValue::create(argument));
   ToLocalFrame(OverlayPage()->MainFrame())
-      ->Script()
+      ->GetScriptController()
       .ExecuteScriptInMainWorld(
           "dispatch(" + command->serialize() + ")",
           ScriptController::kExecuteScriptWhenScriptsDisabled);
@@ -638,7 +638,7 @@
   command->pushValue(protocol::StringValue::create(method));
   command->pushValue(std::move(argument));
   ToLocalFrame(OverlayPage()->MainFrame())
-      ->Script()
+      ->GetScriptController()
       .ExecuteScriptInMainWorld(
           "dispatch(" + command->serialize() + ")",
           ScriptController::kExecuteScriptWhenScriptsDisabled);
@@ -649,7 +649,7 @@
   v8::HandleScope handle_scope(ToIsolate(OverlayMainFrame()));
   v8::Local<v8::Value> string =
       ToLocalFrame(OverlayPage()->MainFrame())
-          ->Script()
+          ->GetScriptController()
           .ExecuteScriptInMainWorldAndReturnValue(
               ScriptSourceCode(script),
               ScriptController::kExecuteScriptWhenScriptsDisabled);
diff --git a/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp b/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
index 0dac0fa..e7465de 100644
--- a/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
+++ b/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
@@ -59,10 +59,11 @@
 
   Vector<v8::Local<v8::Value>> results;
   if (world_id_) {
-    frame->Script().ExecuteScriptInIsolatedWorld(world_id_, sources_, &results);
+    frame->GetScriptController().ExecuteScriptInIsolatedWorld(
+        world_id_, sources_, &results);
   } else {
     v8::Local<v8::Value> script_value =
-        frame->Script().ExecuteScriptInMainWorldAndReturnValue(
+        frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
             sources_.front());
     results.push_back(script_value);
   }
diff --git a/third_party/WebKit/Source/web/WebDevToolsFrontendImpl.cpp b/third_party/WebKit/Source/web/WebDevToolsFrontendImpl.cpp
index 77fe6603..7e0f1ea8 100644
--- a/third_party/WebKit/Source/web/WebDevToolsFrontendImpl.cpp
+++ b/third_party/WebKit/Source/web/WebDevToolsFrontendImpl.cpp
@@ -97,7 +97,7 @@
   script_with_id.Append('(');
   script_with_id.AppendNumber(++last_script_id);
   script_with_id.Append(')');
-  frame->GetFrame()->Script().ExecuteScriptInMainWorld(
+  frame->GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
       script_with_id.ToString());
 }
 
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
index 1fde9f53..6acd683 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
@@ -395,7 +395,7 @@
 
   DEFINE_STATIC_LOCAL(CustomCountHistogram, script_size_histogram,
                       ("ServiceWorker.ScriptSize", 1000, 5000000, 50));
-  script_size_histogram.Count(main_script_loader_->Script().length());
+  script_size_histogram.Count(main_script_loader_->SourceText().length());
   if (main_script_loader_->CachedMetadata()) {
     DEFINE_STATIC_LOCAL(
         CustomCountHistogram, script_cached_metadata_size_histogram,
@@ -455,7 +455,7 @@
   std::unique_ptr<WorkerThreadStartupData> startup_data =
       WorkerThreadStartupData::Create(
           script_url, worker_start_data_.user_agent,
-          main_script_loader_->Script(),
+          main_script_loader_->SourceText(),
           main_script_loader_->ReleaseCachedMetadata(), start_mode,
           document->GetContentSecurityPolicy()->Headers().get(),
           main_script_loader_->GetReferrerPolicy(), starter_origin,
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index 6b1a86a..3177631 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -675,7 +675,7 @@
   TextPosition position(OrdinalNumber::FromOneBasedInt(source.start_line),
                         OrdinalNumber::First());
   v8::HandleScope handle_scope(ToIsolate(GetFrame()));
-  GetFrame()->Script().ExecuteScriptInMainWorld(
+  GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
       ScriptSourceCode(source.code, source.url, position));
 }
 
@@ -690,7 +690,8 @@
   HeapVector<ScriptSourceCode> sources =
       CreateSourcesVector(sources_in, num_sources);
   v8::HandleScope handle_scope(ToIsolate(GetFrame()));
-  GetFrame()->Script().ExecuteScriptInIsolatedWorld(world_id, sources, 0);
+  GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(world_id,
+                                                                 sources, 0);
 }
 
 void WebLocalFrameImpl::SetIsolatedWorldSecurityOrigin(
@@ -755,8 +756,10 @@
 
   TextPosition position(OrdinalNumber::FromOneBasedInt(source.start_line),
                         OrdinalNumber::First());
-  return GetFrame()->Script().ExecuteScriptInMainWorldAndReturnValue(
-      ScriptSourceCode(source.code, source.url, position));
+  return GetFrame()
+      ->GetScriptController()
+      .ExecuteScriptInMainWorldAndReturnValue(
+          ScriptSourceCode(source.code, source.url, position));
 }
 
 void WebLocalFrameImpl::RequestExecuteScriptAndReturnValue(
@@ -799,8 +802,8 @@
 
   if (results) {
     Vector<v8::Local<v8::Value>> script_results;
-    GetFrame()->Script().ExecuteScriptInIsolatedWorld(world_id, sources,
-                                                      &script_results);
+    GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(
+        world_id, sources, &script_results);
     WebVector<v8::Local<v8::Value>> v8_results(script_results.size());
     for (unsigned i = 0; i < script_results.size(); i++)
       v8_results[i] =
@@ -808,7 +811,8 @@
     results->Swap(v8_results);
   } else {
     v8::HandleScope handle_scope(ToIsolate(GetFrame()));
-    GetFrame()->Script().ExecuteScriptInIsolatedWorld(world_id, sources, 0);
+    GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(world_id,
+                                                                   sources, 0);
   }
 }
 
@@ -1940,7 +1944,7 @@
       GetFrame()->GetDocument(), UserGestureToken::kNewGesture));
   v8::HandleScope handle_scope(ToIsolate(GetFrame()));
   v8::Local<v8::Value> result =
-      GetFrame()->Script().ExecuteScriptInMainWorldAndReturnValue(
+      GetFrame()->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
           ScriptSourceCode(script));
   if (result.IsEmpty() || !result->IsString())
     return;
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
index 7802cd4..fe6a27e66 100644
--- a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
@@ -486,7 +486,7 @@
                      : nullptr);
   v8::HandleScope handle_scope(ToIsolate(frame));
   v8::Local<v8::Value> result =
-      frame->Script().ExecuteScriptInMainWorldAndReturnValue(
+      frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
           ScriptSourceCode(script));
 
   // Failure is reported as a null string.
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
index 30b87ee..9f0e096 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
@@ -347,8 +347,8 @@
       WTF::WrapUnique(new WorkerSettings(document->GetSettings()));
   std::unique_ptr<WorkerThreadStartupData> startup_data =
       WorkerThreadStartupData::Create(
-          url_, loading_document_->UserAgent(), main_script_loader_->Script(),
-          nullptr, start_mode,
+          url_, loading_document_->UserAgent(),
+          main_script_loader_->SourceText(), nullptr, start_mode,
           content_security_policy ? content_security_policy->Headers().get()
                                   : nullptr,
           main_script_loader_->GetReferrerPolicy(), starter_origin,
@@ -370,7 +370,7 @@
   worker_thread_ =
       SharedWorkerThread::Create(name_, loader_proxy_, *reporting_proxy_);
   probe::scriptImported(loading_document_, main_script_loader_->Identifier(),
-                        main_script_loader_->Script());
+                        main_script_loader_->SourceText());
   main_script_loader_.Clear();
 
   GetWorkerThread()->Start(std::move(startup_data),
diff --git a/third_party/WebKit/Source/web/tests/ActivityLoggerTest.cpp b/third_party/WebKit/Source/web/tests/ActivityLoggerTest.cpp
index f2aea572..68c96d5 100644
--- a/third_party/WebKit/Source/web/tests/ActivityLoggerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ActivityLoggerTest.cpp
@@ -76,8 +76,10 @@
     V8DOMActivityLogger::SetActivityLogger(kIsolatedWorldId, String(),
                                            WTF::WrapUnique(activity_logger_));
     web_view_helper_.Initialize(true);
-    script_controller_ =
-        &web_view_helper_.WebView()->MainFrameImpl()->GetFrame()->Script();
+    script_controller_ = &web_view_helper_.WebView()
+                              ->MainFrameImpl()
+                              ->GetFrame()
+                              ->GetScriptController();
     FrameTestHelpers::LoadFrame(web_view_helper_.WebView()->MainFrame(),
                                 "about:blank");
   }
diff --git a/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
index 88be435..8a831b9 100644
--- a/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
@@ -805,7 +805,7 @@
   EXPECT_TRUE(frame_element->contentDocument()->View()->CanThrottleRendering());
 
   LocalFrame* local_frame = ToLocalFrame(frame_element->ContentFrame());
-  local_frame->Script().ExecuteScriptInMainWorld(
+  local_frame->GetScriptController().ExecuteScriptInMainWorld(
       "document.body.innerHTML = 'throttled'");
   EXPECT_FALSE(Compositor().NeedsBeginFrame());
 
@@ -996,7 +996,7 @@
   auto* second_frame_element =
       toHTMLIFrameElement(GetDocument().GetElementById("second"));
   LocalFrame* local_frame = ToLocalFrame(second_frame_element->ContentFrame());
-  local_frame->Script().ExecuteScriptInMainWorld(
+  local_frame->GetScriptController().ExecuteScriptInMainWorld(
       "window.requestAnimationFrame(function() {\n"
       "  var throttledFrame = window.parent.frames.first;\n"
       "  throttledFrame.document.documentElement.style = 'margin: 50px';\n"
@@ -1030,7 +1030,7 @@
   LocalFrame* local_frame = ToLocalFrame(frame_element->ContentFrame());
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Value> result =
-      local_frame->Script().ExecuteScriptInMainWorldAndReturnValue(
+      local_frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
           ScriptSourceCode("window.didRaf;"));
   EXPECT_TRUE(result->IsTrue());
 }
diff --git a/third_party/WebKit/Source/web/tests/ResizeObserverTest.cpp b/third_party/WebKit/Source/web/tests/ResizeObserverTest.cpp
index 991b284..ff068ad6 100644
--- a/third_party/WebKit/Source/web/tests/ResizeObserverTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ResizeObserverTest.cpp
@@ -108,16 +108,17 @@
   ASSERT_EQ(observers.size(), 0U);
   v8::HandleScope scope(v8::Isolate::GetCurrent());
 
-  ScriptController& script = GetDocument().ExecutingFrame()->Script();
+  ScriptController& script_controller =
+      GetDocument().ExecutingFrame()->GetScriptController();
 
   //
   // Test whether ResizeObserver is kept alive by direct JS reference
   //
-  script.ExecuteScriptInMainWorldAndReturnValue(
+  script_controller.ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode("var ro = new ResizeObserver( entries => {});"),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   ASSERT_EQ(observers.size(), 1U);
-  script.ExecuteScriptInMainWorldAndReturnValue(
+  script_controller.ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode("ro = undefined;"),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
@@ -127,7 +128,7 @@
   //
   // Test whether ResizeObserver is kept alive by an Element
   //
-  script.ExecuteScriptInMainWorldAndReturnValue(
+  script_controller.ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode("var ro = new ResizeObserver( () => {});"
                        "var el = document.createElement('div');"
                        "ro.observe(el);"
@@ -137,7 +138,7 @@
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
   WebHeap::CollectAllGarbageForTesting();
   ASSERT_EQ(observers.size(), 1U);
-  script.ExecuteScriptInMainWorldAndReturnValue(
+  script_controller.ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode("el = undefined;"),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
diff --git a/third_party/WebKit/public/platform/scheduler/base/task_queue.h b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
index c57d237..da4102e4 100644
--- a/third_party/WebKit/public/platform/scheduler/base/task_queue.h
+++ b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
@@ -150,7 +150,7 @@
   // NOTE: this must be called on the thread this TaskQueue was created by.
   virtual bool HasPendingImmediateWork() const = 0;
 
-  // Returns requested run time of next scheduled wakeup for a delayed task
+  // Returns requested run time of next scheduled wake-up for a delayed task
   // which is not ready to run. If there are no such tasks or the queue is
   // disabled (by a QueueEnabledVoter) it returns base::nullopt.
   // NOTE: this must be called on the thread this TaskQueue was created by.
diff --git a/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h b/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
index dd3e1d2f..b6cdfc76 100644
--- a/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
+++ b/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
@@ -71,8 +71,8 @@
                             const IdleTask& idle_task);
 
   // |idle_task| is eligible to run after the next time an idle period starts
-  // after |delay|.  Note this has after wakeup semantics, i.e. unless something
-  // else wakes the CPU up, this won't run.
+  // after |delay|.  Note this has after wake-up semantics, i.e. unless
+  // something else wakes the CPU up, this won't run.
   virtual void PostDelayedIdleTask(const tracked_objects::Location& from_here,
                                    const base::TimeDelta delay,
                                    const IdleTask& idle_task);
diff --git a/third_party/WebKit/public/platform/scheduler/child/webthread_base.h b/third_party/WebKit/public/platform/scheduler/child/webthread_base.h
index a1d40bf..3b7a4201 100644
--- a/third_party/WebKit/public/platform/scheduler/child/webthread_base.h
+++ b/third_party/WebKit/public/platform/scheduler/child/webthread_base.h
@@ -18,10 +18,17 @@
 class SingleThreadIdleTaskRunner;
 class TaskTimeObserver;
 
+// TODO(scheduler-dev): Do not expose this class in Blink public API.
 class BLINK_PLATFORM_EXPORT WebThreadBase : public WebThread {
  public:
   ~WebThreadBase() override;
 
+  static std::unique_ptr<WebThreadBase> CreateWorkerThread(
+      const char* name,
+      base::Thread::Options options);
+  static std::unique_ptr<WebThreadBase> CreateCompositorThread(
+      base::Thread::Options options);
+
   // WebThread implementation.
   bool IsCurrentThread() const override;
   PlatformThreadId ThreadId() const override = 0;
@@ -43,6 +50,8 @@
   // this thread. Can be called from any thread.
   virtual scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const = 0;
 
+  virtual void Init() = 0;
+
  protected:
   class TaskObserverAdapter;
 
diff --git a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
index cdcc868..85485f3 100644
--- a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
@@ -9,13 +9,14 @@
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "public/platform/scheduler/child/child_scheduler.h"
-#include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
-#include "public/platform/scheduler/renderer/render_widget_scheduling_state.h"
+#include "base/threading/thread.h"
 #include "public/platform/WebCommon.h"
 #include "public/platform/WebInputEvent.h"
 #include "public/platform/WebInputEventResult.h"
 #include "public/platform/WebScheduler.h"
+#include "public/platform/scheduler/child/child_scheduler.h"
+#include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
+#include "public/platform/scheduler/renderer/render_widget_scheduling_state.h"
 #include "v8/include/v8.h"
 
 namespace base {
diff --git a/third_party/WebKit/public/platform/scheduler/utility/webthread_impl_for_utility_thread.h b/third_party/WebKit/public/platform/scheduler/utility/webthread_impl_for_utility_thread.h
index 037ea91..30498aec 100644
--- a/third_party/WebKit/public/platform/scheduler/utility/webthread_impl_for_utility_thread.h
+++ b/third_party/WebKit/public/platform/scheduler/utility/webthread_impl_for_utility_thread.h
@@ -26,6 +26,7 @@
   // WebThreadBase implementation.
   base::SingleThreadTaskRunner* GetTaskRunner() const override;
   scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+  void Init() override;
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index 76587b86..16e16cc 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -56,6 +56,7 @@
     <classpathentry kind="src" path="components/location/android/java/src"/>
     <classpathentry kind="src" path="components/navigation_interception/android/java/src"/>
     <classpathentry kind="src" path="components/ntp_tiles/android/java/src"/>
+    <classpathentry kind="src" path="components/payments/content/android/java/src"/>
     <classpathentry kind="src" path="components/policy/android/java/src"/>
     <classpathentry kind="src" path="components/precache/android/java/src"/>
     <classpathentry kind="src" path="components/precache/android/javatests/src"/>
diff --git a/tools/binary_size/console.py b/tools/binary_size/console.py
index b354a68b..53f7606 100755
--- a/tools/binary_size/console.py
+++ b/tools/binary_size/console.py
@@ -93,17 +93,19 @@
         self._variables['size_info%d' % (i + 1)] = size_info
         self._variables['symbols%d' % (i + 1)] = size_info.symbols
 
-  def _PrintFunc(self, obj, verbose=False, use_pager=None, to_file=None):
+  def _PrintFunc(self, obj, verbose=False, recursive=False, use_pager=None,
+                 to_file=None):
     """Prints out the given Symbol / SymbolGroup / SymbolDiff / SizeInfo.
 
     Args:
       obj: The object to be printed.
       verbose: Show more detailed output.
+      recursive: Print children of nested SymbolGroups.
       use_pager: Pipe output through `less`. Ignored when |obj| is a Symbol.
           default is to automatically pipe when output is long.
       to_file: Rather than print to stdio, write to the given file.
     """
-    lines = describe.GenerateLines(obj, verbose=verbose)
+    lines = describe.GenerateLines(obj, verbose=verbose, recursive=recursive)
     _WriteToStream(lines, use_pager=use_pager, to_file=to_file)
 
   def _ElfPathForSymbol(self, symbol):
@@ -245,7 +247,7 @@
     if not path.endswith('.size'):
       parser.error('All inputs must end with ".size"')
 
-  size_infos = [map2size.Analyze(p) for p in args.inputs]
+  size_infos = [map2size.LoadAndPostProcessSizeInfo(p) for p in args.inputs]
   lazy_paths = paths.LazyPaths(args=args, input_file=args.inputs[0])
   session = _Session(size_infos, lazy_paths)
 
diff --git a/tools/binary_size/create_html_breakdown.py b/tools/binary_size/create_html_breakdown.py
index 44a6d11..ad278b2 100755
--- a/tools/binary_size/create_html_breakdown.py
+++ b/tools/binary_size/create_html_breakdown.py
@@ -14,7 +14,6 @@
 
 import helpers
 import map2size
-import paths
 
 
 # Node dictionary keys. These are output in json read by the webapp so
@@ -167,8 +166,7 @@
 def main(argv):
   parser = argparse.ArgumentParser()
   parser.add_argument('input_file',
-                      help='Path to input file. Can be a linker .map file, or '
-                           'a .size file.')
+                      help='Path to input .size file.')
   parser.add_argument('--report-dir', metavar='PATH', required=True,
                       help='Write output to the specified directory. An HTML '
                             'report is generated here.')
@@ -177,11 +175,9 @@
                            'space)')
   parser.add_argument('--include-symbols', action='store_true',
                       help='Use per-symbol granularity rather than per-file.')
-  paths.AddOptions(parser)
   args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
 
-  lazy_paths = paths.LazyPaths(args=args, input_file=args.input_file)
-  size_info = map2size.Analyze(args.input_file, lazy_paths)
+  size_info = map2size.LoadAndPostProcessSizeInfo(args.input_file)
   symbols = size_info.symbols
   if not args.include_bss:
     symbols = symbols.WhereInSection('b').Inverted()
diff --git a/tools/binary_size/describe.py b/tools/binary_size/describe.py
index d3edd8f..b4cf6a71 100644
--- a/tools/binary_size/describe.py
+++ b/tools/binary_size/describe.py
@@ -29,9 +29,20 @@
   return '%.1fmb' % size
 
 
+def _DiffPrefix(diff, sym):
+  if diff.IsAdded(sym):
+    return '+ '
+  if diff.IsRemoved(sym):
+    return '- '
+  if sym.size:
+    return '~ '
+  return '= '
+
+
 class Describer(object):
-  def __init__(self, verbose=False):
+  def __init__(self, verbose=False, recursive=False):
     self.verbose = verbose
+    self.recursive = recursive
 
   def _DescribeSectionSizes(self, section_sizes):
     relevant_names = models.SECTION_TO_SECTION_NAME.values()
@@ -58,68 +69,76 @@
         yield '    {}: {:,} bytes'.format(name, section_sizes[name])
 
   def _DescribeSymbol(self, sym):
-    # SymbolGroups are passed here when we don't want to expand them.
     if sym.IsGroup():
-      if self.verbose:
-        yield ('{} {:<8} {} (count={})  padding={}  '
-               'size_without_padding={}').format(
-                    sym.section, sym.size, sym.name, len(sym), sym.padding,
-                    sym.size_without_padding)
-      else:
-        yield '{} {:<8} {} (count={})'.format(sym.section, sym.size, sym.name,
-                                              len(sym))
-      return
-
+      address = 'Group'
+    else:
+      address = hex(sym.address)
     if self.verbose:
-      yield '{}@0x{:<8x}  size={}  padding={}  size_without_padding={}'.format(
-          sym.section, sym.address, sym.size, sym.padding,
-          sym.size_without_padding)
+      count_part = '  count=%d' % len(sym) if sym.IsGroup() else ''
+      yield '{}@{:<9s}  size={}  padding={}  size_without_padding={}{}'.format(
+          sym.section, address, sym.size, sym.padding, sym.size_without_padding,
+          count_part)
       yield '    source_path={} \tobject_path={}'.format(
           sym.source_path, sym.object_path)
-      if sym.full_name:
-        yield '    is_anonymous={}  full_name={}'.format(
-            int(sym.is_anonymous), sym.full_name)
       if sym.name:
         yield '    is_anonymous={}  name={}'.format(
             int(sym.is_anonymous), sym.name)
+        if sym.full_name:
+          yield '               full_name={}'.format(sym.full_name)
+      elif sym.full_name:
+        yield '    is_anonymous={}  full_name={}'.format(
+            int(sym.is_anonymous), sym.full_name)
     else:
-      yield '{}@0x{:<8x}  {:<7} {}'.format(
-          sym.section, sym.address, sym.size,
+      yield '{}@{:<9s}  {:<7} {}'.format(
+          sym.section, address, sym.size,
           sym.source_path or sym.object_path or '{no path}')
       if sym.name:
-        yield '    {}'.format(sym.name)
+        count_part = ' (count=%d)' % len(sym) if sym.IsGroup() else ''
+        yield '    {}{}'.format(sym.name, count_part)
 
-  def _DescribeSymbolGroup(self, group, prefix_func=None):
-    total_size = group.size
-    yield 'Showing {:,} symbols with total size: {} bytes'.format(
-        len(group), total_size)
-    code_syms = group.WhereInSection('t')
-    code_size = code_syms.size
-    ro_size = code_syms.Inverted().WhereInSection('r').size
-    yield '.text={:<10} .rodata={:<10} other={:<10} total={}'.format(
-        _PrettySize(code_size), _PrettySize(ro_size),
-        _PrettySize(total_size - code_size - ro_size),
-        _PrettySize(total_size))
-    unique_paths = set(s.object_path for s in group)
-    yield 'Number of object files: {}'.format(len(unique_paths))
-    yield ''
-    yield 'First columns are: running total, type, size'
-
+  def _DescribeSymbolGroupChildren(self, group, indent=0):
     running_total = 0
-    prefix = ''
     sorted_syms = group if group.is_sorted else group.Sorted()
+    is_diff = isinstance(group, models.SymbolDiff)
 
-    prefix = ''
+    indent_prefix = '> ' * indent
+    diff_prefix = ''
     for s in sorted_syms:
       if group.IsBss() or not s.IsBss():
         running_total += s.size
       for l in self._DescribeSymbol(s):
         if l[:4].isspace():
-          yield '{} {}'.format(' ' * (8 + len(prefix)), l)
+          indent_size = 8 + len(indent_prefix) + len(diff_prefix)
+          yield '{} {}'.format(' ' * indent_size, l)
         else:
-          if prefix_func:
-            prefix = prefix_func(s)
-          yield '{}{:8} {}'.format(prefix, running_total, l)
+          if is_diff:
+            diff_prefix = _DiffPrefix(group, s)
+          yield '{}{}{:8} {}'.format(indent_prefix, diff_prefix,
+                                     running_total, l)
+
+      if self.recursive and s.IsGroup():
+        for l in self._DescribeSymbolGroupChildren(s, indent=indent + 1):
+          yield l
+
+  def _DescribeSymbolGroup(self, group):
+    total_size = group.size
+    code_syms = group.WhereInSection('t')
+    code_size = code_syms.size
+    ro_size = code_syms.Inverted().WhereInSection('r').size
+    unique_paths = set(s.object_path for s in group)
+    header_desc = [
+        'Showing {:,} symbols with total size: {} bytes'.format(
+            len(group), total_size),
+        '.text={:<10} .rodata={:<10} other={:<10} total={}'.format(
+            _PrettySize(code_size), _PrettySize(ro_size),
+            _PrettySize(total_size - code_size - ro_size),
+            _PrettySize(total_size)),
+        'Number of object files: {}'.format(len(unique_paths)),
+        '',
+        'First columns are: running total, type, size',
+    ]
+    children_desc = self._DescribeSymbolGroupChildren(group)
+    return itertools.chain(header_desc, children_desc)
 
   def _DescribeSymbolDiff(self, diff):
     header_template = ('{} symbols added (+), {} changed (~), {} removed (-), '
@@ -150,17 +169,8 @@
       path_delta_desc.append('Removed files:')
       path_delta_desc.extend('  ' + p for p in sorted(removed_paths))
 
-    def prefix_func(sym):
-      if diff.IsAdded(sym):
-        return '+ '
-      if diff.IsRemoved(sym):
-        return '- '
-      if sym.size:
-        return '~ '
-      return '= '
-
     diff = diff if self.verbose else diff.WhereNotUnchanged()
-    group_desc = self._DescribeSymbolGroup(diff, prefix_func=prefix_func)
+    group_desc = self._DescribeSymbolGroup(diff)
     return itertools.chain(symbol_delta_desc, path_delta_desc, ('',),
                            group_desc)
 
@@ -211,6 +221,7 @@
 
 def DescribeSizeInfoCoverage(size_info):
   """Yields lines describing how accurate |size_info| is."""
+  symbols = models.SymbolGroup(size_info.raw_symbols)
   for section in models.SECTION_TO_SECTION_NAME:
     if section == 'd':
       expected_size = sum(v for k, v in size_info.section_sizes.iteritems()
@@ -230,7 +241,7 @@
       return (template % (section, size_percent, actual_size, count,
                           expected_size - actual_size, padding))
 
-    in_section = size_info.symbols.WhereInSection(section)
+    in_section = symbols.WhereInSection(section)
     yield one_stat(in_section)
 
     star_syms = in_section.WhereNameMatches(r'^\*')
@@ -264,8 +275,9 @@
   return sorted('%s=%s' % t for t in display_dict.iteritems())
 
 
-def GenerateLines(obj, verbose=False):
-  return Describer(verbose).GenerateLines(obj)
+def GenerateLines(obj, verbose=False, recursive=False):
+  """Returns an iterable of lines (without \n) that describes |obj|."""
+  return Describer(verbose=verbose, recursive=recursive).GenerateLines(obj)
 
 
 def WriteLines(lines, func):
diff --git a/tools/binary_size/file_format.py b/tools/binary_size/file_format.py
index 0db5df2..c9ef495 100644
--- a/tools/binary_size/file_format.py
+++ b/tools/binary_size/file_format.py
@@ -44,7 +44,7 @@
   # Store a single copy of all paths and have them referenced by index.
   # Using an OrderedDict makes the indices more repetitive (better compression).
   path_tuples = collections.OrderedDict.fromkeys(
-      (s.object_path, s.source_path) for s in size_info.symbols)
+      (s.object_path, s.source_path) for s in size_info.raw_symbols)
   for i, key in enumerate(path_tuples):
     path_tuples[key] = i
   file_obj.write('%d\n' % len(path_tuples))
@@ -52,7 +52,8 @@
   _LogSize(file_obj, 'paths')  # For libchrome, adds 200kb.
 
   # Symbol counts by section.
-  by_section = size_info.symbols.GroupBySectionName().SortedByName()
+  by_section = models.SymbolGroup(size_info.raw_symbols)
+  by_section = by_section.GroupBySectionName().SortedByName()
   file_obj.write('%s\n' % '\t'.join(g.name for g in by_section))
   file_obj.write('%s\n' % '\t'.join(str(len(g)) for g in by_section))
 
@@ -127,7 +128,7 @@
   sizes = read_numeric(delta=False)
   path_indices = read_numeric(delta=True)
 
-  symbol_list = [None] * sum(section_counts)
+  raw_symbols = [None] * sum(section_counts)
   symbol_idx = 0
   for section_index, cur_section_name in enumerate(section_names):
     for i in xrange(section_counts[section_index]):
@@ -146,11 +147,10 @@
       new_sym.is_anonymous = is_anonymous
       new_sym.padding = 0  # Derived
       new_sym.full_name = None  # Derived
-      symbol_list[symbol_idx] = new_sym
+      raw_symbols[symbol_idx] = new_sym
       symbol_idx += 1
 
-  symbols = models.SymbolGroup(symbol_list)
-  return models.SizeInfo(section_sizes, symbols, metadata=metadata)
+  return models.SizeInfo(section_sizes, raw_symbols, metadata=metadata)
 
 
 def SaveSizeInfo(size_info, path):
diff --git a/tools/binary_size/integration_test.py b/tools/binary_size/integration_test.py
index 179d22b..40c3d6e 100755
--- a/tools/binary_size/integration_test.py
+++ b/tools/binary_size/integration_test.py
@@ -63,19 +63,38 @@
   def _CloneSizeInfo(self):
     if not IntegrationTest.size_info:
       lazy_paths = paths.LazyPaths(output_directory=_TEST_DATA_DIR)
-      IntegrationTest.size_info = map2size.Analyze(_TEST_MAP_PATH, lazy_paths)
+      IntegrationTest.size_info = (
+          map2size.CreateSizeInfo(_TEST_MAP_PATH, lazy_paths))
     return copy.deepcopy(IntegrationTest.size_info)
 
   @_CompareWithGolden
   def test_Map2Size(self):
     with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
       _RunApp('map2size.py', '--output-directory', _TEST_DATA_DIR,
-              '--map-file', _TEST_MAP_PATH, '', temp_file.name)
-      size_info = map2size.Analyze(temp_file.name)
+              '--map-file', _TEST_MAP_PATH, '--elf-file', '',
+              '--output-file', temp_file.name)
+      size_info = map2size.LoadAndPostProcessSizeInfo(temp_file.name)
+    # Check that saving & loading is the same as directly parsing the .map.
+    expected_size_info = self._CloneSizeInfo()
+    self.assertEquals(expected_size_info.metadata, size_info.metadata)
+    expected = '\n'.join(describe.GenerateLines(
+        expected_size_info, verbose=True, recursive=True)),
+    actual = '\n'.join(describe.GenerateLines(
+        size_info, verbose=True, recursive=True)),
+    self.assertEquals(expected, actual)
+
     sym_strs = (repr(sym) for sym in size_info.symbols)
     stats = describe.DescribeSizeInfoCoverage(size_info)
     return itertools.chain(stats, sym_strs)
 
+  def test_Map2Size_NoSourcePaths(self):
+    # Just tests that it doesn't crash.
+    with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
+      _RunApp('map2size.py', '--no-source-paths',
+              '--map-file', _TEST_MAP_PATH, '--elf-file', '',
+              '--output-file', temp_file.name)
+      map2size.LoadAndPostProcessSizeInfo(temp_file.name)
+
   @_CompareWithGolden
   def test_ConsoleNullDiff(self):
     with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
@@ -96,6 +115,10 @@
     return describe.GenerateLines(diff, verbose=True)
 
   @_CompareWithGolden
+  def test_FullDescription(self):
+    return describe.GenerateLines(self._CloneSizeInfo())
+
+  @_CompareWithGolden
   def test_SymbolGroupMethods(self):
     all_syms = self._CloneSizeInfo().symbols
     global_syms = all_syms.WhereNameMatches('GLOBAL')
diff --git a/tools/binary_size/map2size.py b/tools/binary_size/map2size.py
index 205baffd..95310c3 100755
--- a/tools/binary_size/map2size.py
+++ b/tools/binary_size/map2size.py
@@ -7,6 +7,7 @@
 
 import argparse
 import calendar
+import collections
 import datetime
 import gzip
 import logging
@@ -34,9 +35,9 @@
   return open(path, mode or 'r')
 
 
-def _UnmangleRemainingSymbols(symbol_group, tool_prefix):
+def _UnmangleRemainingSymbols(symbols, tool_prefix):
   """Uses c++filt to unmangle any symbols that need it."""
-  to_process = [s for s in symbol_group if s.name.startswith('_Z')]
+  to_process = [s for s in symbols if s.name.startswith('_Z')]
   if not to_process:
     return
 
@@ -50,7 +51,7 @@
     to_process[i].name = line
 
 
-def _NormalizeNames(symbol_group):
+def _NormalizeNames(symbols):
   """Ensures that all names are formatted in a useful way.
 
   This includes:
@@ -60,9 +61,9 @@
     - Moving "vtable for" and the like to be suffixes rather than prefixes.
   """
   found_prefixes = set()
-  for symbol in symbol_group:
+  for symbol in symbols:
     if symbol.name.startswith('*'):
-      # See comment in _RemoveDuplicatesAndCalculatePadding() about when this
+      # See comment in _CalculatePadding() about when this
       # can happen.
       continue
 
@@ -103,9 +104,9 @@
   logging.debug('Found name prefixes of: %r', found_prefixes)
 
 
-def _NormalizeObjectPaths(symbol_group):
+def _NormalizeObjectPaths(symbols):
   """Ensures that all paths are formatted in a useful way."""
-  for symbol in symbol_group:
+  for symbol in symbols:
     path = symbol.object_path
     if path.startswith('obj/'):
       # Convert obj/third_party/... -> third_party/...
@@ -130,7 +131,7 @@
   return path
 
 
-def _ExtractSourcePaths(symbol_group, output_directory):
+def _ExtractSourcePaths(symbols, output_directory):
   """Fills in the .source_path attribute of all symbols.
 
   Returns True if source paths were found.
@@ -138,7 +139,7 @@
   all_found = True
   mapper = ninja_parser.SourceFileMapper(output_directory)
 
-  for symbol in symbol_group:
+  for symbol in symbols:
     object_path = symbol.object_path
     if symbol.source_path or not object_path:
       continue
@@ -154,15 +155,14 @@
   return all_found
 
 
-def _RemoveDuplicatesAndCalculatePadding(symbol_group):
-  """Removes symbols at the same address and calculates the |padding| field.
+def _CalculatePadding(symbols):
+  """Populates the |padding| field based on symbol addresses.
 
   Symbols must already be sorted by |address|.
   """
-  to_remove = []
   seen_sections = []
-  for i, symbol in enumerate(symbol_group[1:]):
-    prev_symbol = symbol_group[i]
+  for i, symbol in enumerate(symbols[1:]):
+    prev_symbol = symbols[i]
     if prev_symbol.section_name != symbol.section_name:
       assert symbol.section_name not in seen_sections, (
           'Input symbols must be sorted by section, then address.')
@@ -170,12 +170,10 @@
       continue
     if symbol.address <= 0 or prev_symbol.address <= 0:
       continue
-    # Fold symbols that are at the same address (happens in nm output).
+    # Padding-only symbols happen for ** symbol gaps.
     prev_is_padding_only = prev_symbol.size_without_padding == 0
     if symbol.address == prev_symbol.address and not prev_is_padding_only:
-      symbol.size = max(prev_symbol.size, symbol.size)
-      to_remove.add(symbol)
-      continue
+      assert False, 'Found duplicate symbols:\n%r\n%r' % (prev_symbol, symbol)
     # Even with symbols at the same address removed, overlaps can still
     # happen. In this case, padding will be negative (and this is fine).
     padding = symbol.address - prev_symbol.end_address
@@ -200,62 +198,163 @@
     assert symbol.size >= 0, (
         'Symbol has negative size (likely not sorted propertly): '
         '%r\nprev symbol: %r' % (symbol, prev_symbol))
-  # Map files have no overlaps, so worth special-casing the no-op case.
-  if to_remove:
-    logging.info('Removing %d overlapping symbols', len(to_remove))
-    symbol_group -= models.SymbolGroup(to_remove)
 
 
-def Analyze(path, lazy_paths=None):
-  """Returns a SizeInfo for the given |path|.
+def _ClusterSymbols(symbols):
+  """Returns a new list of symbols with some symbols moved into groups.
 
-  Args:
-    path: Can be a .size file, or a .map(.gz). If the latter, then lazy_paths
-        must be provided as well.
+  Groups include:
+   * Symbols that have [clone] in their name (created by compiler optimization).
+   * Star symbols (such as "** merge strings", and "** symbol gap")
   """
-  if path.endswith('.size'):
-    logging.debug('Loading results from: %s', path)
-    size_info = file_format.LoadSizeInfo(path)
-    # Recompute derived values (padding and function names).
-    logging.info('Calculating padding')
-    _RemoveDuplicatesAndCalculatePadding(size_info.symbols)
-    logging.info('Deriving signatures')
-    # Re-parse out function parameters.
-    _NormalizeNames(size_info.symbols)
-    return size_info
-  elif not path.endswith('.map') and not path.endswith('.map.gz'):
-    raise Exception('Expected input to be a .map or a .size')
-  else:
+  # http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-suffix-after-compilation
+  # Example name suffixes:
+  #     [clone .part.322]
+  #     [clone .isra.322]
+  #     [clone .constprop.1064]
+
+  # Step 1: Create name map, find clones, collect star syms into replacements.
+  logging.debug('Creating name -> symbol map')
+  clone_indices = []
+  indices_by_full_name = {}
+  # (name, full_name) -> [(index, sym),...]
+  replacements_by_name = collections.defaultdict(list)
+  for i, symbol in enumerate(symbols):
+    if symbol.name.startswith('**'):
+      # "symbol gap 3" -> "symbol gaps"
+      name = re.sub(r'\s+\d+$', 's', symbol.name)
+      replacements_by_name[(name, None)].append((i, symbol))
+    elif symbol.full_name:
+      if symbol.full_name.endswith(']') and ' [clone ' in symbol.full_name:
+        clone_indices.append(i)
+      else:
+        indices_by_full_name[symbol.full_name] = i
+
+  # Step 2: Collect same-named clone symbols.
+  logging.debug('Grouping all clones')
+  group_names_by_index = {}
+  for i in clone_indices:
+    symbol = symbols[i]
+    # Multiple attributes could exist, so search from left-to-right.
+    stripped_name = symbol.name[:symbol.name.index(' [clone ')]
+    stripped_full_name = symbol.full_name[:symbol.full_name.index(' [clone ')]
+    name_tup = (stripped_name, stripped_full_name)
+    replacement_list = replacements_by_name[name_tup]
+
+    if not replacement_list:
+      # First occurance, check for non-clone symbol.
+      non_clone_idx = indices_by_full_name.get(stripped_name)
+      if non_clone_idx is not None:
+        non_clone_symbol = symbols[non_clone_idx]
+        replacement_list.append((non_clone_idx, non_clone_symbol))
+        group_names_by_index[non_clone_idx] = stripped_name
+
+    replacement_list.append((i, symbol))
+    group_names_by_index[i] = stripped_name
+
+  # Step 3: Undo clustering when length=1.
+  # Removing these groups means Diff() logic must know about [clone] suffix.
+  to_clear = []
+  for name_tup, replacement_list in replacements_by_name.iteritems():
+    if len(replacement_list) == 1:
+      to_clear.append(name_tup)
+  for name_tup in to_clear:
+    del replacements_by_name[name_tup]
+
+  # Step 4: Replace first symbol from each cluster with a SymbolGroup.
+  before_symbol_count = sum(len(x) for x in replacements_by_name.itervalues())
+  logging.debug('Creating %d symbol groups from %d symbols. %d clones had only '
+                'one symbol.', len(replacements_by_name), before_symbol_count,
+                len(to_clear))
+
+  len_delta = len(replacements_by_name) - before_symbol_count
+  grouped_symbols = [None] * (len(symbols) + len_delta)
+  dest_index = 0
+  src_index = 0
+  seen_names = set()
+  replacement_names_by_index = {}
+  for name_tup, replacement_list in replacements_by_name.iteritems():
+    for tup in replacement_list:
+      replacement_names_by_index[tup[0]] = name_tup
+
+  sorted_items = replacement_names_by_index.items()
+  sorted_items.sort(key=lambda tup: tup[0])
+  for index, name_tup in sorted_items:
+    count = index - src_index
+    grouped_symbols[dest_index:dest_index + count] = (
+        symbols[src_index:src_index + count])
+    src_index = index + 1
+    dest_index += count
+    if name_tup not in seen_names:
+      seen_names.add(name_tup)
+      group_symbols = [tup[1] for tup in replacements_by_name[name_tup]]
+      grouped_symbols[dest_index] = models.SymbolGroup(
+          group_symbols, name=name_tup[0], full_name=name_tup[1],
+          section_name=group_symbols[0].section_name)
+      dest_index += 1
+
+  assert len(grouped_symbols[dest_index:None]) == len(symbols[src_index:None])
+  grouped_symbols[dest_index:None] = symbols[src_index:None]
+  logging.debug('Finished making groups.')
+  return grouped_symbols
+
+
+def LoadAndPostProcessSizeInfo(path):
+  """Returns a SizeInfo for the given |path|."""
+  logging.debug('Loading results from: %s', path)
+  size_info = file_format.LoadSizeInfo(path)
+  _PostProcessSizeInfo(size_info)
+  return size_info
+
+
+def _PostProcessSizeInfo(size_info):
+  logging.info('Normalizing symbol names')
+  _NormalizeNames(size_info.raw_symbols)
+  logging.info('Calculating padding')
+  _CalculatePadding(size_info.raw_symbols)
+  logging.info('Grouping decomposed functions')
+  size_info.symbols = models.SymbolGroup(
+      _ClusterSymbols(size_info.raw_symbols))
+  logging.info('Processed %d symbols', len(size_info.raw_symbols))
+
+
+def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False,
+                   raw_only=False):
+  """Creates a SizeInfo from the given map file."""
+  if not no_source_paths:
     # output_directory needed for source file information.
     lazy_paths.VerifyOutputDirectory()
-    # tool_prefix needed for c++filt.
-    lazy_paths.VerifyToolPrefix()
+  # tool_prefix needed for c++filt.
+  lazy_paths.VerifyToolPrefix()
 
-    with _OpenMaybeGz(path) as map_file:
-      section_sizes, symbols = linker_map_parser.MapFileParser().Parse(map_file)
-    size_info = models.SizeInfo(section_sizes, models.SymbolGroup(symbols))
+  with _OpenMaybeGz(map_path) as map_file:
+    section_sizes, raw_symbols = (
+        linker_map_parser.MapFileParser().Parse(map_file))
 
-    # Map file for some reason doesn't unmangle all names.
-    logging.info('Calculating padding')
-    _RemoveDuplicatesAndCalculatePadding(size_info.symbols)
-    # Unmangle prints its own log statement.
-    _UnmangleRemainingSymbols(size_info.symbols, lazy_paths.tool_prefix)
+  if not no_source_paths:
     logging.info('Extracting source paths from .ninja files')
-    all_found = _ExtractSourcePaths(size_info.symbols,
-                                    lazy_paths.output_directory)
+    all_found = _ExtractSourcePaths(raw_symbols, lazy_paths.output_directory)
     assert all_found, (
         'One or more source file paths could not be found. Likely caused by '
         '.ninja files being generated at a different time than the .map file.')
-    # Resolve paths prints its own log statement.
-    logging.info('Normalizing names')
-    _NormalizeNames(size_info.symbols)
-    logging.info('Normalizing paths')
-    _NormalizeObjectPaths(size_info.symbols)
+  # Map file for some reason doesn't unmangle all names.
+  # Unmangle prints its own log statement.
+  _UnmangleRemainingSymbols(raw_symbols, lazy_paths.tool_prefix)
+  logging.info('Normalizing object paths')
+  _NormalizeObjectPaths(raw_symbols)
+  size_info = models.SizeInfo(section_sizes, raw_symbols)
 
-  if logging.getLogger().isEnabledFor(logging.INFO):
+  # Name normalization not strictly required, but makes for smaller files.
+  if raw_only:
+    logging.info('Normalizing symbol names')
+    _NormalizeNames(size_info.raw_symbols)
+  else:
+    _PostProcessSizeInfo(size_info)
+
+  if logging.getLogger().isEnabledFor(logging.DEBUG):
     for line in describe.DescribeSizeInfoCoverage(size_info):
       logging.info(line)
-  logging.info('Finished analyzing %d symbols', len(size_info.symbols))
+  logging.info('Recorded info for %d symbols', len(size_info.raw_symbols))
   return size_info
 
 
@@ -303,22 +402,28 @@
 
 def main(argv):
   parser = argparse.ArgumentParser(argv)
-  parser.add_argument('elf_file', help='Path to input ELF file.')
-  parser.add_argument('output_file', help='Path to output .size(.gz) file.')
+  parser.add_argument('--elf-file', required=True,
+                      help='Path to input ELF file. Currently used for '
+                           'capturing metadata. Pass "" to skip metadata '
+                           'collection.')
   parser.add_argument('--map-file',
                       help='Path to input .map(.gz) file. Defaults to '
                            '{{elf_file}}.map(.gz)?')
+  parser.add_argument('--output-file', required=True,
+                      help='Path to output .size file.')
+  parser.add_argument('--no-source-paths', action='store_true',
+                      help='Do not use .ninja files to map '
+                           'object_path -> source_path')
   paths.AddOptions(parser)
   args = helpers.AddCommonOptionsAndParseArgs(parser, argv)
   if not args.output_file.endswith('.size'):
     parser.error('output_file must end with .size')
 
   if args.map_file:
+    if (not args.map_file.endswith('.map')
+        and not args.map_file.endswith('.map.gz')):
+      parser.error('Expected --map-file to end with .map or .map.gz')
     map_file_path = args.map_file
-  elif args.elf_file.endswith('.size'):
-    # Allow a .size file to be passed as input as well. Useful for measuring
-    # serialization speed.
-    pass
   else:
     map_file_path = args.elf_file + '.map'
     if not os.path.exists(map_file_path):
@@ -328,7 +433,7 @@
 
   lazy_paths = paths.LazyPaths(args=args, input_file=args.elf_file)
   metadata = None
-  if args.elf_file and not args.elf_file.endswith('.size'):
+  if args.elf_file:
     logging.debug('Constructing metadata')
     git_rev = _DetectGitRevision(os.path.dirname(args.elf_file))
     build_id = BuildIdFromElf(args.elf_file, lazy_paths.tool_prefix)
@@ -349,9 +454,12 @@
         models.METADATA_GN_ARGS: gn_args,
     }
 
-  size_info = Analyze(map_file_path, lazy_paths)
+  size_info = CreateSizeInfo(map_file_path, lazy_paths,
+                             no_source_paths=args.no_source_paths,
+                             raw_only=True)
 
   if metadata:
+    size_info.metadata = metadata
     logging.debug('Validating section sizes')
     elf_section_sizes = _SectionSizesFromElf(args.elf_file,
                                              lazy_paths.tool_prefix)
@@ -359,8 +467,6 @@
       assert v == size_info.section_sizes.get(k), (
           'ELF file and .map file do not match.')
 
-    size_info.metadata = metadata
-
   logging.info('Recording metadata: \n  %s',
                '\n  '.join(describe.DescribeMetadata(size_info.metadata)))
   logging.info('Saving result to %s', args.output_file)
diff --git a/tools/binary_size/models.py b/tools/binary_size/models.py
index ee0481e..178ab0b8 100644
--- a/tools/binary_size/models.py
+++ b/tools/binary_size/models.py
@@ -51,19 +51,26 @@
 
   Fields:
     section_sizes: A dict of section_name -> size.
-    symbols: A SymbolGroup with all symbols in it.
+    raw_symbols: A flat list of all symbols.
+    symbols: A SymbolGroup containing raw_symbols, but with some Symbols grouped
+        into sub-SymbolGroups.
     metadata: A dict.
   """
   __slots__ = (
       'section_sizes',
+      'raw_symbols',
       'symbols',
       'metadata',
   )
 
   """Root size information."""
-  def __init__(self, section_sizes, symbols, metadata=None):
+  def __init__(self, section_sizes, raw_symbols, grouped_symbols=None,
+               metadata=None):
     self.section_sizes = section_sizes  # E.g. {'.text': 0}
-    self.symbols = symbols  # List of symbols sorted by address per-section.
+    # List of symbols sorted by address per-section.
+    self.raw_symbols = raw_symbols
+    # Root SymbolGroup. Cloned symbols grouped together within sub-SymbolGroups.
+    self.symbols = grouped_symbols
     self.metadata = metadata or {}
 
 
@@ -130,7 +137,12 @@
     Keys are not guaranteed to be unique within a SymbolGroup. For example, it
     is common to have multiple "** merge strings" symbols, which will have a
     common key."""
-    return (self.section_name, self.full_name or self.name)
+    stripped_full_name = self.full_name
+    if stripped_full_name:
+      clone_idx = stripped_full_name.find(' [clone ')
+      if clone_idx != -1:
+        stripped_full_name = stripped_full_name[:clone_idx]
+    return (self.section_name, stripped_full_name or self.name)
 
 
 class Symbol(BaseSymbol):
@@ -193,18 +205,20 @@
       '_size',
       '_symbols',
       '_filtered_symbols',
+      'full_name',
       'name',
       'section_name',
       'is_sorted',
   )
 
   def __init__(self, symbols, filtered_symbols=None, name=None,
-               section_name=None, is_sorted=False):
+               full_name=None, section_name=None, is_sorted=False):
     self._padding = None
     self._size = None
     self._symbols = symbols
     self._filtered_symbols = filtered_symbols or []
     self.name = name or ''
+    self.full_name = full_name
     self.section_name = section_name or '.*'
     self.is_sorted = is_sorted
 
@@ -248,23 +262,24 @@
 
   @property
   def address(self):
-    return 0
-
-  @property
-  def full_name(self):
-    return None
+    first = self._symbols[0].address
+    return first if all(s.address == first for s in self._symbols) else 0
 
   @property
   def is_anonymous(self):
-    return False
+    first = self._symbols[0].is_anonymous
+    return first if all(
+        s.is_anonymous == first for s in self._symbols) else False
 
   @property
   def object_path(self):
-    return None
+    first = self._symbols[0].object_path
+    return first if all(s.object_path == first for s in self._symbols) else None
 
   @property
   def source_path(self):
-    return None
+    first = self._symbols[0].source_path
+    return first if all(s.source_path == first for s in self._symbols) else None
 
   @property
   def size(self):
@@ -510,14 +525,16 @@
       '_removed_ids',
   )
 
-  def __init__(self, added, removed, similar):
+  def __init__(self, added, removed, similar, name=None, full_name=None,
+               section_name=None):
     self._added_ids = set(id(s) for s in added)
     self._removed_ids = set(id(s) for s in removed)
     symbols = []
     symbols.extend(added)
     symbols.extend(removed)
     symbols.extend(similar)
-    super(SymbolDiff, self).__init__(symbols)
+    super(SymbolDiff, self).__init__(symbols, name=name, full_name=full_name,
+                                     section_name=section_name)
 
   def __repr__(self):
     return '%s(%d added, %d removed, %d changed, %d unchanged, size=%d)' % (
@@ -588,46 +605,73 @@
     return SizeInfoDiff(section_sizes, symbol_diff, old.metadata, new.metadata)
 
   assert isinstance(new, SymbolGroup) and isinstance(old, SymbolGroup)
+  return _DiffSymbols(new, old)
+
+
+def _NegateAll(symbols):
+  ret = []
+  for symbol in symbols:
+    if symbol.IsGroup():
+      duped = SymbolDiff([], _NegateAll(symbol), [], name=symbol.name,
+                         full_name=symbol.full_name,
+                         section_name=symbol.section_name)
+    else:
+      duped = copy.copy(symbol)
+      duped.size = -duped.size
+      duped.padding = -duped.padding
+    ret.append(duped)
+  return ret
+
+
+def _DiffSymbols(new_group, old_group):
   symbols_by_key = collections.defaultdict(list)
-  for s in old:
+  for s in old_group:
     symbols_by_key[s._Key()].append(s)
 
   added = []
-  removed = []
   similar = []
   # For similar symbols, padding is zeroed out. In order to not lose the
   # information entirely, store it in aggregate.
   padding_by_section_name = collections.defaultdict(int)
-  for new_sym in new:
+  for new_sym in new_group:
     matching_syms = symbols_by_key.get(new_sym._Key())
     if matching_syms:
       old_sym = matching_syms.pop(0)
-      # More stable/useful to compare size without padding.
-      size_diff = (new_sym.size_without_padding -
-                   old_sym.size_without_padding)
-      merged_sym = Symbol(new_sym.section_name, size_diff,
-                          address=new_sym.address, name=new_sym.name,
-                          source_path=new_sym.source_path,
-                          object_path=new_sym.object_path,
-                          full_name=new_sym.full_name,
-                          is_anonymous=new_sym.is_anonymous)
+      if old_sym.IsGroup() and new_sym.IsGroup():
+        merged_sym = _DiffSymbols(new_sym, old_sym)
+      else:
+        size_diff = new_sym.size_without_padding - old_sym.size_without_padding
+        merged_sym = Symbol(new_sym.section_name, size_diff,
+                            address=new_sym.address, name=new_sym.name,
+                            source_path=new_sym.source_path,
+                            object_path=new_sym.object_path,
+                            full_name=new_sym.full_name,
+                            is_anonymous=new_sym.is_anonymous)
+
+        # Diffs are more stable when comparing size without padding, except when
+        # the symbol is a padding-only symbol.
+        if new_sym.size_without_padding == 0 and size_diff == 0:
+          merged_sym.padding = new_sym.padding - old_sym.padding
+        else:
+          padding_by_section_name[new_sym.section_name] += (
+              new_sym.padding - old_sym.padding)
+
       similar.append(merged_sym)
-      padding_by_section_name[new_sym.section_name] += (
-          new_sym.padding - old_sym.padding)
     else:
       added.append(new_sym)
 
+  removed = []
   for remaining_syms in symbols_by_key.itervalues():
-    for old_sym in remaining_syms:
-      duped = copy.copy(old_sym)
-      duped.size = -duped.size
-      duped.padding = -duped.padding
-      removed.append(duped)
+    if remaining_syms:
+      removed.extend(_NegateAll(remaining_syms))
 
   for section_name, padding in padding_by_section_name.iteritems():
-    similar.append(Symbol(section_name, padding,
-                          name="** aggregate padding of diff'ed symbols"))
-  return SymbolDiff(added, removed, similar)
+    if padding != 0:
+      similar.append(Symbol(section_name, padding,
+                            name="** aggregate padding of diff'ed symbols"))
+  return SymbolDiff(added, removed, similar, name=new_group.name,
+                    full_name=new_group.full_name,
+                    section_name=new_group.section_name)
 
 
 def _ExtractPrefixBeforeSeparator(string, separator, count=1):
diff --git a/tools/binary_size/testdata/ActualDiff.golden b/tools/binary_size/testdata/ActualDiff.golden
index 89a39260..2299ddb1 100644
--- a/tools/binary_size/testdata/ActualDiff.golden
+++ b/tools/binary_size/testdata/ActualDiff.golden
@@ -49,155 +49,135 @@
     .strtab: 0 bytes
     .symtab: 0 bytes
 
-3 symbols added (+), 1 changed (~), 2 removed (-), 39 unchanged (=)
+3 symbols added (+), 1 changed (~), 2 removed (-), 32 unchanged (=)
 0 object files added, 1 removed
 Removed files:
   third_party/ffmpeg/libffmpeg_internal.a/fft_fixed.o
 
-Showing 45 symbols with total size: -10 bytes
+Showing 38 symbols with total size: -10 bytes
 .text=-10 bytes  .rodata=0 bytes    other=0 bytes    total=-10 bytes
-Number of object files: 9
+Number of object files: 10
 
 First columns are: running total, type, size
-~      -10 t@0x28d900    size=-10  padding=0  size_without_padding=-10
+~      -10 t@0x28d900   size=-10  padding=0  size_without_padding=-10
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=startup._GLOBAL__sub_I_page_allocator.cc
-=      -10 r@0x284e364   size=0  padding=0  size_without_padding=0
+=      -10 r@0x284e364  size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
-=      -10 r@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 d@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 d@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 d@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 t@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 r@0x266e600   size=0  padding=0  size_without_padding=0
+=      -10 r@Group      size=0  padding=0  size_without_padding=0  count=2
                source_path= 	object_path=
                is_anonymous=0  name=** merge strings
-=      -10 r@0x284e518   size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** merge strings
-=      -10 t@0x28f000    size=0  padding=0  size_without_padding=0
-               source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-               is_anonymous=0  name=** symbol gap 0
-=      -10 t@0x2a1000    size=0  padding=0  size_without_padding=0
-               source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
-               is_anonymous=0  name=** symbol gap 1
-=      -10 d@0x2cd84e0   size=0  padding=0  size_without_padding=0
+=      -10 t@Group      size=0  padding=0  size_without_padding=0  count=2
+               source_path=None 	object_path=None
+               is_anonymous=0  name=** symbol gaps
+=      -10 d@0x2cd84e0  size=0  padding=0  size_without_padding=0
                source_path= 	object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o
                is_anonymous=0  name=.Lswitch.table.45
-=      -10 d@0x2c176f0   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2c176f0  size=0  padding=0  size_without_padding=0
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=0  name=ChromeMainDelegate [vtable]
-=      -10 d@0x2cd8500   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2cd8500  size=0  padding=0  size_without_padding=0
                source_path=third_party/paint.cc 	object_path=third_party/WebKit.a/PaintChunker.o
                is_anonymous=0  name=ChromeMainDelegateAndroid [vtable]
-=      -10 r@0x284e370   size=0  padding=0  size_without_padding=0
+=      -10 r@0x284e370  size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=Name
-=      -10 d@0x2de70a0   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2de70a0  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=1  name=base::android::g_renderer_histogram_code
-=      -10 r@0x28f3480   size=0  padding=0  size_without_padding=0
+=      -10 r@0x28f3480  size=0  padding=0  size_without_padding=0
                source_path=third_party/paint.cc 	object_path=third_party/WebKit.a/PaintChunker.o
-               is_anonymous=1  full_name=blink::CSSValueKeywordsHash::findValueImpl(char const*, unsigned int)::value_word_list
                is_anonymous=1  name=blink::CSSValueKeywordsHash::findValueImpl::value_word_list
-=      -10 t@0x2a0020    size=0  padding=0  size_without_padding=0
+                          full_name=blink::CSSValueKeywordsHash::findValueImpl(char const*, unsigned int)::value_word_list
+=      -10 t@0x2a0020   size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
-               is_anonymous=0  full_name=blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&)
                is_anonymous=0  name=blink::ContiguousContainerBase::ContiguousContainerBase
-=      -10 t@0x2a0000    size=0  padding=0  size_without_padding=0
+                          full_name=blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&)
+=      -10 t@0x2a0000   size=0  padding=0  size_without_padding=0
                source_path=third_party/paint.cc 	object_path=third_party/WebKit.a/PaintChunker.o
-               is_anonymous=0  full_name=blink::ContiguousContainerBase::shrinkToFit()
                is_anonymous=0  name=blink::ContiguousContainerBase::shrinkToFit
-=      -10 t@0x2a1000    size=0  padding=0  size_without_padding=0
+                          full_name=blink::ContiguousContainerBase::shrinkToFit()
+=      -10 t@0x2a0010   size=0  padding=0  size_without_padding=0
+               source_path=third_party/paint.cc 	object_path=third_party/WebKit.a/PaintChunker.o
+               is_anonymous=0  name=blink::ContiguousContainerBase::shrinkToFit [clone .part.1234] [clone .isra.2]
+                          full_name=blink::ContiguousContainerBase::shrinkToFit() [clone .part.1234] [clone .isra.2]
+=      -10 t@0x2a1000   size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
-               is_anonymous=1  full_name=blink::PaintChunker::releasePaintChunks()
-               is_anonymous=1  name=blink::PaintChunker::releasePaintChunks
-=      -10 d@0x2c17740   size=0  padding=0  size_without_padding=0
+               is_anonymous=1  name=blink::PaintChunker::releasePaintChunks [clone .part.1]
+                          full_name=blink::PaintChunker::releasePaintChunks() [clone .part.1]
+=      -10 d@0x2c17740  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=0  name=chrome::mojom::FieldTrialRecorderProxy [vtable]
-=      -10 d@0x2c17728   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2c17728  size=0  padding=0  size_without_padding=0
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=0  name=chrome::mojom::FieldTrialRecorderRequestValidator [vtable]
-=      -10 r@0x284e398   size=0  padding=0  size_without_padding=0
+=      -10 r@0x284e398  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=0  name=chrome::mojom::FilePatcher::Name_
-=      -10 t@0x28d964    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28d964   size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
-               is_anonymous=0  full_name=extFromUUseMapping(signed char, unsigned int, int)
                is_anonymous=0  name=extFromUUseMapping
-=      -10 t@0x28d98a    size=0  padding=0  size_without_padding=0
+                          full_name=extFromUUseMapping(signed char, unsigned int, int)
+=      -10 t@0x28d98a   size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
-               is_anonymous=0  full_name=extFromUUseMapping(aj, int)
                is_anonymous=0  name=extFromUUseMapping
-=      -10 d@0x2de7000   size=0  padding=0  size_without_padding=0
+                          full_name=extFromUUseMapping(aj, int)
+=      -10 d@0x2de7000  size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=google::protobuf::internal::pLinuxKernelCmpxchg
-=      -10 d@0x2de7004   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2de7004  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=0  name=google::protobuf::internal::pLinuxKernelMemoryBarrier
-=      -10 r@0x28f3450   size=0  padding=0  size_without_padding=0
+=      -10 r@0x28f3450  size=0  padding=0  size_without_padding=0
                source_path=third_party/paint.cc 	object_path=third_party/WebKit.a/PaintChunker.o
                is_anonymous=1  name=kAnimationFrameTimeHistogramClassPath
-=      -10 d@0x2cd8550   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2cd8550  size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=kMethodsAnimationFrameTimeHistogram
-=      -10 d@0x2cd84f0   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2cd84f0  size=0  padding=0  size_without_padding=0
                source_path= 	object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o
                is_anonymous=1  name=kSystemClassPrefixes
-=      -10 d@0x2cd8538   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2cd8538  size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=mojo::MessageReceiver [vtable]
-=      -10 d@0x2de7008   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2de7008  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=0  name=rel._ZN4base7androidL22kBaseRegisteredMethodsE
-=      -10 d@0x2de70a4   size=0  padding=0  size_without_padding=0
+=      -10 d@0x2de70a4  size=0  padding=0  size_without_padding=0
                source_path=third_party/container.c 	object_path=third_party/WebKit.a/ContiguousContainer.o
                is_anonymous=0  name=rel.local._ZN4base7android12_GLOBAL__N_124g_library_version_numberE
-=      -10 t@0x28f1c8    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28f1c8   size=0  padding=0  size_without_padding=0
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=0  name=startup._GLOBAL__sub_I_SkDeviceProfile.cpp
-=      -10 t@0x28f1e0    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28f1e0   size=0  padding=0  size_without_padding=0
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=0  name=startup._GLOBAL__sub_I_SkDiscardableMemoryPool.cpp
-=      -10 t@0x28d910    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28d910   size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=startup._GLOBAL__sub_I_bbr_sender.cc
-=      -10 t@0x28d948    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28d948   size=0  padding=0  size_without_padding=0
                source_path=base/page_allocator.cc 	object_path=base/base/page_allocator.o
                is_anonymous=0  name=startup._GLOBAL__sub_I_pacing_sender.cc
-=      -10 t@0x28f000    size=0  padding=0  size_without_padding=0
+=      -10 t@0x28f000   size=0  padding=0  size_without_padding=0
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-               is_anonymous=0  full_name=ucnv_extMatchFromU(int const*, int, unsigned short const*, int, unsigned short const*, int, unsigned int*, signed char, signed char)
                is_anonymous=0  name=ucnv_extMatchFromU
--      -10 b@0x0         size=-262144  padding=0  size_without_padding=-262144
+                          full_name=ucnv_extMatchFromU(int const*, int, unsigned short const*, int, unsigned short const*, int, unsigned int*, signed char, signed char)
+-      -10 b@0x0        size=-262144  padding=0  size_without_padding=-262144
                source_path=third_party/fft_float.cc 	object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o
                is_anonymous=0  name=ff_cos_131072
--      -10 b@0x0         size=-131072  padding=0  size_without_padding=-131072
+-      -10 b@0x0        size=-131072  padding=0  size_without_padding=-131072
                source_path=third_party/fft_fixed.cc 	object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_fixed.o
                is_anonymous=0  name=ff_cos_131072_fixed
-+      -10 b@0x2dffe80   size=200  padding=196  size_without_padding=4
++      -10 b@0x2dffe80  size=200  padding=196  size_without_padding=4
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
-               is_anonymous=0  full_name=SaveHistogram(_JNIEnv*, base::android::JavaParamRef<_jobject*> const&, base::android::JavaParamRef<_jstring*> const&, base::android::JavaParamRef<_jlongArray*> const&, int)::atomic_histogram_pointer
                is_anonymous=0  name=SaveHistogram::atomic_histogram_pointer
-+      -10 b@0x2dffda0   size=28  padding=0  size_without_padding=28
+                          full_name=SaveHistogram(_JNIEnv*, base::android::JavaParamRef<_jobject*> const&, base::android::JavaParamRef<_jstring*> const&, base::android::JavaParamRef<_jlongArray*> const&, int)::atomic_histogram_pointer
++      -10 b@0x2dffda0  size=28  padding=0  size_without_padding=28
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=0  name=g_chrome_content_browser_client
-+      -10 b@0x2dffe84   size=4  padding=0  size_without_padding=4
++      -10 b@0x2dffe84  size=4  padding=0  size_without_padding=4
                source_path=third_party/icu/ucnv_ext.c 	object_path=third_party/icu/icuuc/ucnv_ext.o
                is_anonymous=1  name=g_AnimationFrameTimeHistogram_clazz
-=      -10 b@0x0         size=0  padding=0  size_without_padding=0
-               source_path= 	object_path=
-               is_anonymous=0  name=** aggregate padding of diff'ed symbols
-=      -10 b@0x0         size=0  padding=0  size_without_padding=0
+=      -10 b@0x0        size=0  padding=0  size_without_padding=0
                source_path=third_party/fft_float.cc 	object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o
                is_anonymous=0  name=ff_cos_65536
diff --git a/tools/binary_size/testdata/ConsoleNullDiff.golden b/tools/binary_size/testdata/ConsoleNullDiff.golden
index 12180caa..4e420427 100644
--- a/tools/binary_size/testdata/ConsoleNullDiff.golden
+++ b/tools/binary_size/testdata/ConsoleNullDiff.golden
@@ -10,7 +10,7 @@
     .rodata: 0 bytes (0.0%)
     .text: 0 bytes (0.0%)
 
-0 symbols added (+), 0 changed (~), 0 removed (-), 45 unchanged (not shown)
+0 symbols added (+), 0 changed (~), 0 removed (-), 38 unchanged (not shown)
 0 object files added, 0 removed
 
 Showing 0 symbols with total size: 0 bytes
diff --git a/tools/binary_size/testdata/FullDescription.golden b/tools/binary_size/testdata/FullDescription.golden
new file mode 100644
index 0000000..1b61c38b4
--- /dev/null
+++ b/tools/binary_size/testdata/FullDescription.golden
@@ -0,0 +1,90 @@
+Metadata:
+
+Section Sizes (Total=43,785,380 bytes):
+    .bss: 1,300,456 bytes (not included in totals)
+    .data: 101,768 bytes (0.2%)
+    .data.rel.ro: 1,065,224 bytes (2.4%)
+    .data.rel.ro.local: 790,024 bytes (1.8%)
+    .rodata: 5,927,652 bytes (13.5%)
+    .text: 35,900,712 bytes (82.0%)
+
+Showing 38 symbols with total size: 2652506 bytes
+.text=10.3kb     .rodata=2.52mb     other=388 bytes  total=2.53mb
+Number of object files: 10
+
+First columns are: running total, type, size
+ 2641394 r@Group      2641394 {no path}
+             ** merge strings (count=2)
+ 2651152 t@Group      9758    {no path}
+             ** symbol gaps (count=2)
+ 2651600 t@0x28f000   448     third_party/icu/ucnv_ext.c
+             ucnv_extMatchFromU
+ 2651752 d@0x2de7008  152     third_party/container.c
+             rel._ZN4base7androidL22kBaseRegisteredMethodsE
+ 2651846 t@0x2a1000   94      third_party/container.c
+             blink::PaintChunker::releasePaintChunks [clone .part.1]
+ 2651902 d@0x2c176f0  56      third_party/icu/ucnv_ext.c
+             ChromeMainDelegate [vtable]
+ 2651958 d@0x2cd8500  56      third_party/paint.cc
+             ChromeMainDelegateAndroid [vtable]
+ 2652014 t@0x28d910   56      base/page_allocator.cc
+             startup._GLOBAL__sub_I_bbr_sender.cc
+ 2652069 r@0x28f3450  55      third_party/paint.cc
+             kAnimationFrameTimeHistogramClassPath
+ 2652113 r@0x284e370  44      base/page_allocator.cc
+             Name
+ 2652151 t@0x28d964   38      base/page_allocator.cc
+             extFromUUseMapping
+ 2652183 r@0x284e398  32      third_party/container.c
+             chrome::mojom::FilePatcher::Name_
+ 2652215 t@0x28d98a   32      base/page_allocator.cc
+             extFromUUseMapping
+ 2652243 t@0x2a0020   28      third_party/container.c
+             blink::ContiguousContainerBase::ContiguousContainerBase
+ 2652271 t@0x28f1c8   28      third_party/icu/ucnv_ext.c
+             startup._GLOBAL__sub_I_SkDeviceProfile.cpp
+ 2652299 t@0x28d948   28      base/page_allocator.cc
+             startup._GLOBAL__sub_I_pacing_sender.cc
+ 2652323 d@0x2c17740  24      third_party/container.c
+             chrome::mojom::FieldTrialRecorderProxy [vtable]
+ 2652347 d@0x2c17728  24      third_party/icu/ucnv_ext.c
+             chrome::mojom::FieldTrialRecorderRequestValidator [vtable]
+ 2652371 d@0x2cd8538  24      base/page_allocator.cc
+             mojo::MessageReceiver [vtable]
+ 2652395 t@0x28f1e0   24      third_party/icu/ucnv_ext.c
+             startup._GLOBAL__sub_I_SkDiscardableMemoryPool.cpp
+ 2652411 d@0x2cd84e0  16      third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o
+             .Lswitch.table.45
+ 2652427 t@0x2a0000   16      third_party/paint.cc
+             blink::ContiguousContainerBase::shrinkToFit
+ 2652443 t@0x28d900   16      base/page_allocator.cc
+             startup._GLOBAL__sub_I_page_allocator.cc
+ 2652455 t@0x2a0010   12      third_party/paint.cc
+             blink::ContiguousContainerBase::shrinkToFit [clone .part.1234] [clone .isra.2]
+ 2652467 d@0x2cd8550  12      base/page_allocator.cc
+             kMethodsAnimationFrameTimeHistogram
+ 2652478 r@0x284e364  11      base/page_allocator.cc
+ 2652486 d@0x2cd84f0  8       third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o
+             kSystemClassPrefixes
+ 2652490 d@0x2de70a0  4       third_party/container.c
+             base::android::g_renderer_histogram_code
+ 2652494 r@0x28f3480  4       third_party/paint.cc
+             blink::CSSValueKeywordsHash::findValueImpl::value_word_list
+ 2652498 d@0x2de7000  4       base/page_allocator.cc
+             google::protobuf::internal::pLinuxKernelCmpxchg
+ 2652502 d@0x2de7004  4       third_party/container.c
+             google::protobuf::internal::pLinuxKernelMemoryBarrier
+ 2652506 d@0x2de70a4  4       third_party/container.c
+             rel.local._ZN4base7android12_GLOBAL__N_124g_library_version_numberE
+ 2652506 b@0x0        262144  third_party/fft_float.cc
+             ff_cos_131072
+ 2652506 b@0x0        131072  third_party/fft_fixed.cc
+             ff_cos_131072_fixed
+ 2652506 b@0x0        131072  third_party/fft_float.cc
+             ff_cos_65536
+ 2652506 b@0x2dffe80  200     third_party/icu/ucnv_ext.c
+             SaveHistogram::atomic_histogram_pointer
+ 2652506 b@0x2dffda0  28      third_party/icu/ucnv_ext.c
+             g_chrome_content_browser_client
+ 2652506 b@0x2dffe84  4       third_party/icu/ucnv_ext.c
+             g_AnimationFrameTimeHistogram_clazz
diff --git a/tools/binary_size/testdata/Map2Size.golden b/tools/binary_size/testdata/Map2Size.golden
index 7365359..e44faea 100644
--- a/tools/binary_size/testdata/Map2Size.golden
+++ b/tools/binary_size/testdata/Map2Size.golden
@@ -3,9 +3,9 @@
 + Section r has 0.0% of 146 bytes accounted for from 5 symbols. 5927506 bytes are unaccounted for. Padding accounts for 14 bytes
 Section b has 0.0% of 0 bytes accounted for from 6 symbols. 1300456 bytes are unaccounted for. Padding accounts for 196 bytes
 Section d has 0.0% of 388 bytes accounted for from 13 symbols. 1956628 bytes are unaccounted for. Padding accounts for 0 bytes
-Section t has 0.0% of 10578 bytes accounted for from 13 symbols. 35890134 bytes are unaccounted for. Padding accounts for 9774 bytes
+Section t has 0.0% of 10578 bytes accounted for from 14 symbols. 35890134 bytes are unaccounted for. Padding accounts for 9774 bytes
 + Without 2 merge sections and 0 anonymous entries (accounting for 9758 bytes):
-+ Section t has 0.0% of 820 bytes accounted for from 11 symbols. 35899892 bytes are unaccounted for. Padding accounts for 16 bytes
++ Section t has 0.0% of 820 bytes accounted for from 12 symbols. 35899892 bytes are unaccounted for. Padding accounts for 16 bytes
 .bss@0(size_without_padding=262144,padding=0,name=ff_cos_131072,path=third_party/fft_float.cc,anon=0)
 .bss@0(size_without_padding=131072,padding=0,name=ff_cos_131072_fixed,path=third_party/fft_fixed.cc,anon=0)
 .bss@0(size_without_padding=131072,padding=0,name=ff_cos_65536,path=third_party/fft_float.cc,anon=0)
@@ -25,11 +25,10 @@
 .data.rel.ro.local@2c17740(size_without_padding=24,padding=0,name=chrome::mojom::FieldTrialRecorderProxy [vtable],path=third_party/container.c,anon=0)
 .data.rel.ro.local@2cd84e0(size_without_padding=16,padding=0,name=.Lswitch.table.45,path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,anon=0)
 .data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,name=kSystemClassPrefixes,path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,anon=1)
-.rodata@266e600(size_without_padding=1965409,padding=0,name=** merge strings,path=,anon=0)
+Group(name=** merge strings,count=2,size=2641394)
 .rodata@284e364(size_without_padding=8,padding=3,name=,path=base/page_allocator.cc,anon=0)
 .rodata@284e370(size_without_padding=40,padding=4,name=Name,path=base/page_allocator.cc,anon=0)
 .rodata@284e398(size_without_padding=32,padding=0,name=chrome::mojom::FilePatcher::Name_,path=third_party/container.c,anon=0)
-.rodata@284e518(size_without_padding=675633,padding=352,name=** merge strings,path=,anon=0)
 .rodata@28f3450(size_without_padding=48,padding=7,name=kAnimationFrameTimeHistogramClassPath,path=third_party/paint.cc,anon=1)
 .rodata@28f3480(size_without_padding=4,padding=0,name=blink::CSSValueKeywordsHash::findValueImpl::value_word_list,path=third_party/paint.cc,anon=1)
 .text@28d900(size_without_padding=16,padding=0,name=startup._GLOBAL__sub_I_page_allocator.cc,path=base/page_allocator.cc,anon=0)
@@ -37,11 +36,11 @@
 .text@28d948(size_without_padding=28,padding=0,name=startup._GLOBAL__sub_I_pacing_sender.cc,path=base/page_allocator.cc,anon=0)
 .text@28d964(size_without_padding=38,padding=0,name=extFromUUseMapping,path=base/page_allocator.cc,anon=0)
 .text@28d98a(size_without_padding=32,padding=0,name=extFromUUseMapping,path=base/page_allocator.cc,anon=0)
-.text@28f000(size_without_padding=0,padding=5718,name=** symbol gap 0,path=third_party/icu/ucnv_ext.c,anon=0)
+Group(name=** symbol gaps,count=2,size=9758)
 .text@28f000(size_without_padding=448,padding=0,name=ucnv_extMatchFromU,path=third_party/icu/ucnv_ext.c,anon=0)
 .text@28f1c8(size_without_padding=20,padding=8,name=startup._GLOBAL__sub_I_SkDeviceProfile.cpp,path=third_party/icu/ucnv_ext.c,anon=0)
 .text@28f1e0(size_without_padding=20,padding=4,name=startup._GLOBAL__sub_I_SkDiscardableMemoryPool.cpp,path=third_party/icu/ucnv_ext.c,anon=0)
-.text@2a0000(size_without_padding=28,padding=0,name=blink::ContiguousContainerBase::shrinkToFit,path=third_party/paint.cc,anon=0)
+.text@2a0000(size_without_padding=16,padding=0,name=blink::ContiguousContainerBase::shrinkToFit,path=third_party/paint.cc,anon=0)
+.text@2a0010(size_without_padding=12,padding=0,name=blink::ContiguousContainerBase::shrinkToFit [clone .part.1234] [clone .isra.2],path=third_party/paint.cc,anon=0)
 .text@2a0020(size_without_padding=24,padding=4,name=blink::ContiguousContainerBase::ContiguousContainerBase,path=third_party/container.c,anon=0)
-.text@2a1000(size_without_padding=0,padding=4040,name=** symbol gap 1,path=third_party/container.c,anon=0)
-.text@2a1000(size_without_padding=94,padding=0,name=blink::PaintChunker::releasePaintChunks,path=third_party/container.c,anon=1)
+.text@2a1000(size_without_padding=94,padding=0,name=blink::PaintChunker::releasePaintChunks [clone .part.1],path=third_party/container.c,anon=1)
diff --git a/tools/binary_size/testdata/SymbolGroupMethods.golden b/tools/binary_size/testdata/SymbolGroupMethods.golden
index e59d05c..1ec51c4 100644
--- a/tools/binary_size/testdata/SymbolGroupMethods.golden
+++ b/tools/binary_size/testdata/SymbolGroupMethods.golden
@@ -1,58 +1,85 @@
 GroupByNamespace()
 Showing 10 symbols with total size: 2652506 bytes
 .text=0 bytes    .rodata=0 bytes    other=2.53mb     total=2.53mb
-Number of object files: 1
+Number of object files: 5
 
 First columns are: running total, type, size
- 2652236 * 2652236  {global} (count=27)
- 2652330 * 94       blink::PaintChunker (count=1)
- 2652386 * 56       blink::ContiguousContainerBase (count=2)
- 2652434 * 48       chrome::mojom (count=2)
- 2652466 * 32       chrome::mojom::FilePatcher (count=1)
- 2652490 * 24       mojo (count=1)
- 2652498 * 8        google::protobuf::internal (count=2)
- 2652502 * 4        base::android (count=1)
- 2652506 * 4        blink::CSSValueKeywordsHash::findValueImpl (count=1)
- 2652506 * 0        SaveHistogram (count=1)
+ 2652236 *@Group      2652236 {no path}
+             {global} (count=25)
+ 2652330 *@Group      94      third_party/container.c
+             blink::PaintChunker (count=1)
+ 2652386 *@Group      56      {no path}
+             blink::ContiguousContainerBase (count=3)
+ 2652434 *@Group      48      {no path}
+             chrome::mojom (count=2)
+ 2652466 *@Group      32      third_party/container.c
+             chrome::mojom::FilePatcher (count=1)
+ 2652490 *@Group      24      base/page_allocator.cc
+             mojo (count=1)
+ 2652498 *@Group      8       {no path}
+             google::protobuf::internal (count=2)
+ 2652502 *@Group      4       third_party/container.c
+             base::android (count=1)
+ 2652506 *@Group      4       third_party/paint.cc
+             blink::CSSValueKeywordsHash::findValueImpl (count=1)
+ 2652506 *@Group      0       third_party/icu/ucnv_ext.c
+             SaveHistogram (count=1)
 GroupByNamespace(depth=1)
 Showing 7 symbols with total size: 2652506 bytes
 .text=0 bytes    .rodata=0 bytes    other=2.53mb     total=2.53mb
-Number of object files: 1
+Number of object files: 4
 
 First columns are: running total, type, size
- 2652236 * 2652236  {global} (count=27)
- 2652390 * 154      blink (count=4)
- 2652470 * 80       chrome (count=3)
- 2652494 * 24       mojo (count=1)
- 2652502 * 8        google (count=2)
- 2652506 * 4        base (count=1)
- 2652506 * 0        SaveHistogram (count=1)
+ 2652236 *@Group      2652236 {no path}
+             {global} (count=25)
+ 2652390 *@Group      154     {no path}
+             blink (count=5)
+ 2652470 *@Group      80      {no path}
+             chrome (count=3)
+ 2652494 *@Group      24      base/page_allocator.cc
+             mojo (count=1)
+ 2652502 *@Group      8       {no path}
+             google (count=2)
+ 2652506 *@Group      4       third_party/container.c
+             base (count=1)
+ 2652506 *@Group      0       third_party/icu/ucnv_ext.c
+             SaveHistogram (count=1)
 GroupByNamespace(depth=1, fallback=None)
 Showing 7 symbols with total size: 2652506 bytes
 .text=0 bytes    .rodata=0 bytes    other=2.53mb     total=2.53mb
-Number of object files: 1
+Number of object files: 4
 
 First columns are: running total, type, size
- 2652236 * 2652236   (count=27)
- 2652390 * 154      blink (count=4)
- 2652470 * 80       chrome (count=3)
- 2652494 * 24       mojo (count=1)
- 2652502 * 8        google (count=2)
- 2652506 * 4        base (count=1)
- 2652506 * 0        SaveHistogram (count=1)
+ 2652236 *@Group      2652236 {no path}
+ 2652390 *@Group      154     {no path}
+             blink (count=5)
+ 2652470 *@Group      80      {no path}
+             chrome (count=3)
+ 2652494 *@Group      24      base/page_allocator.cc
+             mojo (count=1)
+ 2652502 *@Group      8       {no path}
+             google (count=2)
+ 2652506 *@Group      4       third_party/container.c
+             base (count=1)
+ 2652506 *@Group      0       third_party/icu/ucnv_ext.c
+             SaveHistogram (count=1)
 GroupByNamespace(depth=1, min_count=2)
 Showing 7 symbols with total size: 2652506 bytes
 .text=0 bytes    .rodata=0 bytes    other=2.53mb     total=2.53mb
 Number of object files: 4
 
 First columns are: running total, type, size
- 2652236 * 2652236  {global} (count=27)
- 2652390 * 154      blink (count=4)
- 2652470 * 80       chrome (count=3)
- 2652494 d@0x2cd8538   24      base/page_allocator.cc
+ 2652236 *@Group      2652236 {no path}
+             {global} (count=25)
+ 2652390 *@Group      154     {no path}
+             blink (count=5)
+ 2652470 *@Group      80      {no path}
+             chrome (count=3)
+ 2652494 d@0x2cd8538  24      base/page_allocator.cc
              mojo::MessageReceiver [vtable]
- 2652502 * 8        google (count=2)
- 2652506 d@0x2de70a0   4       third_party/container.c
+ 2652502 *@Group      8       {no path}
+             google (count=2)
+ 2652506 d@0x2de70a0  4       third_party/container.c
              base::android::g_renderer_histogram_code
- 2652506 b@0x2dffe80   200     third_party/icu/ucnv_ext.c
+ 2652506 b@0x2dffe80  200     third_party/icu/ucnv_ext.c
              SaveHistogram::atomic_histogram_pointer
diff --git a/tools/binary_size/testdata/test.map b/tools/binary_size/testdata/test.map
index cc15b5d..13c5e11 100644
--- a/tools/binary_size/testdata/test.map
+++ b/tools/binary_size/testdata/test.map
@@ -90,15 +90,18 @@
  .text.startup._GLOBAL__sub_I_SkDiscardableMemoryPool.cpp
                 0x0028f1e0       0x14 obj/third_party/icu/icuuc/ucnv_ext.o
  .text._ZN5blink23ContiguousContainerBase11shrinkToFitEv
-                0x002a0000       0x1c obj/third_party/WebKit.a(PaintChunker.o)
+                0x002a0000       0x10 obj/third_party/WebKit.a(PaintChunker.o)
                 0x002a0001                blink::ContiguousContainerBase::shrinkToFit()
+ .text._ZN5blink23ContiguousContainerBase11shrinkToFitEv2
+                0x002a0010       0xc obj/third_party/WebKit.a(PaintChunker.o)
+                0x002a0011                blink::ContiguousContainerBase::shrinkToFit() [clone .part.1234] [clone .isra.2]
  .text._ZN5blink23ContiguousContainerBaseC2EOS0_
                 0xffffffffffffffff       0x18 obj/third_party/WebKit.a(ContiguousContainer.o)
                 0x002a0021                blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&)
                 0x002a0021                blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&)
  .text._ZN5blink12PaintChunker18releasePaintChunksEv
                 0x002a1000       0x5e obj/third_party/WebKit.a(ContiguousContainer.o)
-                0x002a1001                (anonymous namespace)::blink::PaintChunker::releasePaintChunks()
+                0x002a1001                (anonymous namespace)::blink::PaintChunker::releasePaintChunks() [clone .part.1]
 
 .ARM.exidx      0x024ca628   0x1771c8
  .ARM.exidx.text.startup._GLOBAL__sub_I_page_allocator.cc
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 8e4ebd7..8dbe6de 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -39,7 +39,7 @@
   bool bool_value = false;
   if (value->GetAsList(&list_value)) {
     for (const auto& v : *list_value) {
-      PrintValue(v.get(), indentLevel);
+      PrintValue(&v, indentLevel);
     }
   } else if (value->GetAsString(&string_value)) {
     OutputString(indent);
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index 39dd079..e9691aaa 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -515,7 +515,7 @@
   FilterAndPrintTargets(targets, &tmp);
   for (const auto& value : tmp) {
     std::string string;
-    value->GetAsString(&string);
+    value.GetAsString(&string);
     if (indent)
       OutputString("  ");
     OutputString(string);
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index aa22ea1b..0cde6cab 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -947,7 +947,8 @@
       .Concat(self._GenerateStringToEnumConversion(item_type,
                                                    '(it)',
                                                    'tmp',
-                                                   failure_value))
+                                                   failure_value,
+                                                   is_ptr=False))
       .Append('%s%spush_back(tmp);' % (dst_var, accessor))
       .Eblock('}')
     )
@@ -957,7 +958,8 @@
                                       type_,
                                       src_var,
                                       dst_var,
-                                      failure_value):
+                                      failure_value,
+                                      is_ptr=True):
     """Returns Code that converts a string type in |src_var| to an enum with
     type |type_| in |dst_var|. In the generated code, if |src_var| is not
     a valid enum name then the function will return |failure_value|.
@@ -969,11 +971,14 @@
     cpp_type_namespace = ''
     if type_.namespace != self._namespace:
       cpp_type_namespace = '%s::' % type_.namespace.unix_name
+    accessor = '->' if is_ptr else '.'
     (c.Append('std::string %s;' % enum_as_string)
-      .Sblock('if (!%s->GetAsString(&%s)) {' % (src_var, enum_as_string))
+      .Sblock('if (!%s%sGetAsString(&%s)) {' % (src_var,
+                                                accessor,
+                                                enum_as_string))
       .Concat(self._GenerateError(
         '"\'%%(key)s\': expected string, got " + ' +
-        self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
+        self._util_cc_helper.GetValueTypeString('%%(src_var)s', is_ptr)))
       .Append('return %s;' % failure_value)
       .Eblock('}')
       .Append('%s = %sParse%s(%s);' % (dst_var,
diff --git a/tools/json_schema_compiler/util.h b/tools/json_schema_compiler/util.h
index d7862ce..8d206a66 100644
--- a/tools/json_schema_compiler/util.h
+++ b/tools/json_schema_compiler/util.h
@@ -102,7 +102,7 @@
   out->clear();
   T item;
   for (const auto& value : list) {
-    if (!PopulateItem(*value, &item))
+    if (!PopulateItem(value, &item))
       return false;
     // T might not be movable, but in that case it should be copyable, and this
     // will still work.
@@ -121,7 +121,7 @@
   out->clear();
   T item;
   for (const auto& value : list) {
-    if (!PopulateItem(*value, &item, error))
+    if (!PopulateItem(value, &item, error))
       return false;
     out->push_back(std::move(item));
   }
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c6e1467..cbc2af4 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -34769,22 +34769,35 @@
   </summary>
 </histogram>
 
+<histogram name="Net.JobControllerSet.CountOfJobController"
+    units="job_controllers">
+  <owner>zhongyi@chromium.org</owner>
+  <summary>
+    This counts number of all the job controllers that are still alive if the
+    count is a multiple of 100: 100, 200, 300, etc.
+  </summary>
+</histogram>
+
+<histogram name="Net.JobControllerSet.CountOfJobControllerAtShutDown"
+    units="job_controllers">
+  <owner>zhongyi@chromium.org</owner>
+  <summary>
+    This counts number of all the job controllers that are still alive.
+  </summary>
+</histogram>
+
 <histogram name="Net.JobControllerSet.CountOfNonPreconnectAltJob"
     units="alt_jobs">
   <owner>zhongyi@chromium.org</owner>
   <summary>
-    This counts number of alternative jobs which are still alive when
-    HttpStreamFactory is destructed.
+    This counts number of alternative jobs which are still alive.
   </summary>
 </histogram>
 
 <histogram name="Net.JobControllerSet.CountOfNonPreconnectMainJob"
     units="main_jobs">
   <owner>zhongyi@chromium.org</owner>
-  <summary>
-    This counts number of main jobs which are still alive when HttpStreamFactory
-    is destructed.
-  </summary>
+  <summary>This counts number of main jobs which are still alive.</summary>
 </histogram>
 
 <histogram name="Net.JobControllerSet.CountOfPreconnect"
@@ -34792,7 +34805,7 @@
   <owner>zhongyi@chromium.org</owner>
   <summary>
     This counts number of job controllers which are used for preconnect and are
-    still alive when HttpStreamFactory is destructed.
+    still alive.
   </summary>
 </histogram>
 
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 8d21159..0c8de0a 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -88,6 +88,7 @@
 page_cycler_v2_site_isolation.basic_oopif,nasko@chromium.org,
 performance_browser_tests,"hubbe@chromium.org, justinlin@chromium.org, miu@chromium.org",
 power.android_acceptance,perezju@chromium.org,
+power.idle_platform,,
 power.steady_state,,
 power.top_10,,
 power.top_25,,
diff --git a/tools/perf/benchmarks/power.py b/tools/perf/benchmarks/power.py
index 358c08e..7cc17e1 100644
--- a/tools/perf/benchmarks/power.py
+++ b/tools/perf/benchmarks/power.py
@@ -7,6 +7,8 @@
 from measurements import power
 import page_sets
 from telemetry import benchmark
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.web_perf import timeline_based_measurement
 
 
 @benchmark.Enabled('android')
@@ -147,6 +149,7 @@
   def Name(cls):
     return 'power.trivial_pages'
 
+
 @benchmark.Enabled('mac')
 class PowerSteadyStatePages(perf_benchmark.PerfBenchmark):
   """Measure power consumption for real web sites in steady state (no user
@@ -157,3 +160,42 @@
   @classmethod
   def Name(cls):
     return 'power.steady_state'
+
+
+class IdlePlatformBenchmark(perf_benchmark.PerfBenchmark):
+  """Idle platform benchmark.
+
+  This benchmark just starts up tracing agents and lets the platform sit idle.
+  Our power benchmarks are prone to noise caused by other things running on the
+  system. This benchmark is intended to help find the sources of noise.
+  """
+  def CreateTimelineBasedMeasurementOptions(self):
+    options = timeline_based_measurement.Options(
+        chrome_trace_category_filter.ChromeTraceCategoryFilter())
+    # Enable CPU tracing when the bug is resolved.
+    # https://github.com/catapult-project/catapult/issues/3463
+    options.config.enable_battor_trace = True
+    # Atrace tracing agent autodetects if its android and only runs if it is.
+    options.config.enable_atrace_trace = True
+    options.config.enable_chrome_trace = False
+    options.SetTimelineBasedMetrics([
+        'clockSyncLatencyMetric',
+        'powerMetric',
+        'tracingMetric'
+    ])
+    return options
+
+  @classmethod
+  def ShouldDisable(cls, possible_browser):
+    return not possible_browser.platform.HasBattOrConnected()
+
+  def CreateStorySet(self, options):
+    return page_sets.IdleStorySet()
+
+  @classmethod
+  def ShouldTearDownStateAfterEachStoryRun(cls):
+    return True
+
+  @classmethod
+  def Name(cls):
+    return 'power.idle_platform'
diff --git a/tools/perf/page_sets/idle_platform.py b/tools/perf/page_sets/idle_platform.py
new file mode 100644
index 0000000..b6fffd4
--- /dev/null
+++ b/tools/perf/page_sets/idle_platform.py
@@ -0,0 +1,41 @@
+# 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 time
+
+from telemetry.page import shared_page_state
+from telemetry import story
+
+
+class _IdleSharedState(shared_page_state.SharedPageState):
+  def __init__(self, test, finder_options, story_set):
+    super(_IdleSharedState, self).__init__(test, finder_options, story_set)
+    self._current_story = None
+
+  def WillRunStory(self, current_story):
+    self._current_story = current_story
+    assert self.platform.tracing_controller.is_tracing_running
+
+  def RunStory(self, _):
+    self._current_story.Run(self)
+
+  def DidRunStory(self, _):
+    self._current_story = None
+
+
+class _IdleStory(story.Story):
+  def __init__(self, name, duration):
+    super(_IdleStory, self).__init__(
+        shared_state_class=_IdleSharedState, name=name)
+    self._duration = duration
+
+  def Run(self, shared_state):
+    time.sleep(self._duration)
+
+
+class IdleStorySet(story.StorySet):
+  def __init__(self):
+    super(IdleStorySet, self).__init__()
+    self.AddStory(_IdleStory('IdleStory_10s', 10))
+    self.AddStory(_IdleStory('IdleStory_60s', 60))
+    self.AddStory(_IdleStory('IdleStory_120s', 120))
diff --git a/tools/perf/page_sets/system_health/story_tags.py b/tools/perf/page_sets/system_health/story_tags.py
index 1c345f5..71d5c51 100644
--- a/tools/perf/page_sets/system_health/story_tags.py
+++ b/tools/perf/page_sets/system_health/story_tags.py
@@ -12,12 +12,12 @@
 # A story can have multiple tags. All the tags should be noun.
 
 AUDIO_PLAYBACK = Tag(
-    'audio-playback', 'Story has audio playing.')
+    'audio_playback', 'Story has audio playing.')
 CANVAS_ANIMATION = Tag(
-    'canvas-animation', 'Story has animations that are implemented using '
+    'canvas_animation', 'Story has animations that are implemented using '
     'html5 canvas.')
 CSS_ANIMATION = Tag(
-    'css-animation', 'Story has animations that are implemented using CSS.')
+    'css_animation', 'Story has animations that are implemented using CSS.')
 EXTENSION = Tag(
     'extension', 'Story has browser with extension installed.')
 IMAGES = Tag(
@@ -26,22 +26,22 @@
     'international', 'Story has navigations to websites with content in non '
     'English languages.')
 JAVASCRIPT_HEAVY = Tag(
-    'javascript-heavy', 'Story has navigations to websites with heavy usages '
+    'javascript_heavy', 'Story has navigations to websites with heavy usages '
     'of JavaScript. The story uses 20Mb+ memory for javascript and local '
     'run with "v8" category enabled also shows the trace has js slices across '
     'the whole run.')
 SCROLL = Tag(
     'scroll', 'Story has scroll gestures & scroll animation.')
 PINCH_ZOOM = Tag(
-    'pinch-zoom', 'Story has pinch zoom gestures & pinch zoom animation.')
+    'pinch_zoom', 'Story has pinch zoom gestures & pinch zoom animation.')
 TABS_SWITCHING = Tag(
-    'tabs-switching', 'Story has multi tabs and tabs switching action.')
+    'tabs_switching', 'Story has multi tabs and tabs switching action.')
 VIDEO_PLAYBACK = Tag(
-    'video-playback', 'Story has video playing.')
+    'video_playback', 'Story has video playing.')
 WEBGL = Tag(
     'webgl', 'Story has sites with heavy uses of WebGL.')
 WEB_STORAGE = Tag(
-    'web-storage', 'Story has sites with heavy uses of Web storage.')
+    'web_storage', 'Story has sites with heavy uses of Web storage.')
 
 
 def _ExtractAllTags():
diff --git a/ui/app_list/search/history_data_store.cc b/ui/app_list/search/history_data_store.cc
index 1eb92b0..d080fa6 100644
--- a/ui/app_list/search/history_data_store.cc
+++ b/ui/app_list/search/history_data_store.cc
@@ -35,7 +35,7 @@
   for (base::ListValue::const_iterator it = list->begin(); it != list->end();
        ++it) {
     std::string str;
-    if (!(*it)->GetAsString(&str))
+    if (!it->GetAsString(&str))
       return;
 
     results.push_back(str);
diff --git a/ui/display/manager/json_converter.cc b/ui/display/manager/json_converter.cc
index 213f7e4..2520d1d 100644
--- a/ui/display/manager/json_converter.cc
+++ b/ui/display/manager/json_converter.cc
@@ -125,7 +125,7 @@
   output->reserve(list->GetSize());
   for (const auto& list_item : *list) {
     const base::DictionaryValue* item_values = nullptr;
-    if (!list_item->GetAsDictionary(&item_values))
+    if (!list_item.GetAsDictionary(&item_values))
       return false;
 
     DisplayPlacement item;
diff --git a/ui/views/animation/square_ink_drop_ripple.cc b/ui/views/animation/square_ink_drop_ripple.cc
index 94fa10d..ea1e6a0 100644
--- a/ui/views/animation/square_ink_drop_ripple.cc
+++ b/ui/views/animation/square_ink_drop_ripple.cc
@@ -170,26 +170,8 @@
                                          const gfx::Point& center_point,
                                          SkColor color,
                                          float visible_opacity)
-    : SquareInkDropRipple(large_size,
-                          large_corner_radius,
-                          small_size,
-                          small_corner_radius,
-                          center_point,
-                          center_point,
-                          color,
-                          visible_opacity) {}
-
-SquareInkDropRipple::SquareInkDropRipple(const gfx::Size& large_size,
-                                         int large_corner_radius,
-                                         const gfx::Size& small_size,
-                                         int small_corner_radius,
-                                         const gfx::Point& initial_center_point,
-                                         const gfx::Point& target_center_point,
-                                         SkColor color,
-                                         float visible_opacity)
     : activated_shape_(ROUNDED_RECT),
       visible_opacity_(visible_opacity),
-      target_center_point_(target_center_point),
       large_size_(large_size),
       large_corner_radius_(large_corner_radius),
       small_size_(small_size),
@@ -209,7 +191,7 @@
   root_layer_.SetBounds(gfx::Rect(large_size_));
 
   gfx::Transform transform;
-  transform.Translate(initial_center_point.x(), initial_center_point.y());
+  transform.Translate(center_point.x(), center_point.y());
   root_layer_.SetTransform(transform);
 
   SetStateToHidden();
@@ -297,10 +279,6 @@
                           GetAnimationDuration(ACTION_PENDING_TRANSFORM),
                           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(target_center_point_,
-                         GetAnimationDuration(ACTION_PENDING_TRANSFORM),
-                         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
-                         gfx::Tween::EASE_IN, animation_observer);
       break;
     case InkDropState::ACTION_TRIGGERED: {
       DCHECK(old_ink_drop_state == InkDropState::HIDDEN ||
@@ -320,10 +298,6 @@
                           GetAnimationDuration(ACTION_TRIGGERED_TRANSFORM),
                           ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(target_center_point_,
-                         GetAnimationDuration(ACTION_TRIGGERED_TRANSFORM),
-                         ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
-                         gfx::Tween::EASE_IN, animation_observer);
       break;
     }
     case InkDropState::ALTERNATE_ACTION_PENDING:
@@ -338,10 +312,6 @@
                           GetAnimationDuration(ALTERNATE_ACTION_PENDING),
                           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(target_center_point_,
-                         GetAnimationDuration(ALTERNATE_ACTION_PENDING),
-                         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
-                         gfx::Tween::EASE_IN, animation_observer);
       break;
     case InkDropState::ALTERNATE_ACTION_TRIGGERED: {
       DCHECK_EQ(InkDropState::ALTERNATE_ACTION_PENDING, old_ink_drop_state)
@@ -361,11 +331,6 @@
                                           ALTERNATE_ACTION_TRIGGERED_TRANSFORM),
                           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(
-          target_center_point_,
-          GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM),
-          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
-          gfx::Tween::EASE_IN, animation_observer);
       break;
     }
     case InkDropState::ACTIVATED: {
@@ -385,10 +350,6 @@
             transforms, GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM),
             ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
             gfx::Tween::EASE_IN_OUT, animation_observer);
-        AnimateCenterPoint(target_center_point_,
-                           GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM),
-                           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
-                           gfx::Tween::EASE_IN, animation_observer);
       } else if (old_ink_drop_state == InkDropState::ACTION_PENDING) {
         rect_transform_preemption_strategy =
             ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
@@ -399,10 +360,6 @@
                           GetAnimationDuration(ACTIVATED_RECT_TRANSFORM),
                           rect_transform_preemption_strategy,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(target_center_point_,
-                         GetAnimationDuration(ACTIVATED_RECT_TRANSFORM),
-                         rect_transform_preemption_strategy,
-                         gfx::Tween::EASE_IN, animation_observer);
       break;
     }
     case InkDropState::DEACTIVATED: {
@@ -421,10 +378,6 @@
                           GetAnimationDuration(DEACTIVATED_TRANSFORM),
                           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
                           gfx::Tween::EASE_IN_OUT, animation_observer);
-      AnimateCenterPoint(target_center_point_,
-                         GetAnimationDuration(DEACTIVATED_TRANSFORM),
-                         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
-                         gfx::Tween::EASE_IN, animation_observer);
       break;
     }
   }
@@ -445,29 +398,6 @@
     painted_layers_[i]->GetAnimator()->AbortAllAnimations();
 }
 
-void SquareInkDropRipple::AnimateCenterPoint(
-    const gfx::Point& center_point,
-    base::TimeDelta duration,
-    ui::LayerAnimator::PreemptionStrategy preemption_strategy,
-    gfx::Tween::Type tween,
-    ui::LayerAnimationObserver* observer) {
-  ui::LayerAnimator* animator = root_layer_.GetAnimator();
-  ui::ScopedLayerAnimationSettings animation(animator);
-  animation.SetPreemptionStrategy(preemption_strategy);
-  animation.SetTweenType(tween);
-  gfx::Transform transform;
-  transform.Translate(target_center_point_.x(), target_center_point_.y());
-  std::unique_ptr<ui::LayerAnimationElement> element =
-      ui::LayerAnimationElement::CreateTransformElement(transform, duration);
-  ui::LayerAnimationSequence* sequence =
-      new ui::LayerAnimationSequence(std::move(element));
-
-  if (observer)
-    sequence->AddObserver(observer);
-
-  animator->StartAnimation(sequence);
-}
-
 void SquareInkDropRipple::AnimateToTransforms(
     const InkDropTransforms transforms,
     base::TimeDelta duration,
diff --git a/ui/views/animation/square_ink_drop_ripple.h b/ui/views/animation/square_ink_drop_ripple.h
index bd13b75..99913360 100644
--- a/ui/views/animation/square_ink_drop_ripple.h
+++ b/ui/views/animation/square_ink_drop_ripple.h
@@ -60,15 +60,6 @@
                       const gfx::Point& center_point,
                       SkColor color,
                       float visible_opacity);
-
-  SquareInkDropRipple(const gfx::Size& large_size,
-                      int large_corner_radius,
-                      const gfx::Size& small_size,
-                      int small_corner_radius,
-                      const gfx::Point& initial_center_point,
-                      const gfx::Point& target_center_point,
-                      SkColor color,
-                      float visible_opacity);
   ~SquareInkDropRipple() override;
 
   void set_activated_shape(ActivatedShape shape) { activated_shape_ = shape; }
@@ -108,17 +99,6 @@
   void SetStateToHidden() override;
   void AbortAllAnimations() override;
 
-  // Animates the |root_layer_| to the specified |center_point|. The animation
-  // will be configured with the given |duration|, |tween|, and
-  // |preemption_strategy| values. The |observer| will be added to all
-  // LayerAnimationSequences if not null.
-  void AnimateCenterPoint(
-      const gfx::Point& center_point,
-      base::TimeDelta duration,
-      ui::LayerAnimator::PreemptionStrategy preemption_strategy,
-      gfx::Tween::Type tween,
-      ui::LayerAnimationObserver* observer);
-
   // Animates all of the painted shape layers to the specified |transforms|. The
   // animation will be configured with the given |duration|, |tween|, and
   // |preemption_strategy| values. The |observer| will be added to all
@@ -179,26 +159,23 @@
   ActivatedShape activated_shape_;
 
   // Ink drop opacity when it is visible.
-  float visible_opacity_;
-
-  // The center point that the ripple will animate to.
-  gfx::Point target_center_point_;
+  const float visible_opacity_;
 
   // Maximum size that an ink drop will be drawn to for any InkDropState whose
   // final frame should be large.
-  gfx::Size large_size_;
+  const gfx::Size large_size_;
 
   // Corner radius used to draw the rounded rectangles corner for any
   // InkDropState whose final frame should be large.
-  int large_corner_radius_;
+  const int large_corner_radius_;
 
   // Maximum size that an ink drop will be drawn to for any InkDropState whose
   // final frame should be small.
-  gfx::Size small_size_;
+  const gfx::Size small_size_;
 
   // Corner radius used to draw the rounded rectangles corner for any
   // InkDropState whose final frame should be small.
-  int small_corner_radius_;
+  const int small_corner_radius_;
 
   // ui::LayerDelegate to paint circles for all the circle layers.
   std::unique_ptr<CircleLayerDelegate> circle_layer_delegate_;