diff --git a/DEPS b/DEPS
index 8e98445..a0cf8672 100644
--- a/DEPS
+++ b/DEPS
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '794607025b69599dd2607391cb13e51f39423f5d',
+  'angle_revision': '982f6e0125af4eaa40ee05157d27cbbd9259d969',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index c686e6b..70490754 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -191,11 +191,7 @@
 # These assets are needed by both monochrome and stand alone WebView, but not by
 # Chrome.
 android_assets("monochrome_webview_assets") {
-  sources = [
-    webview_license_path,
-  ]
   deps = [
-    ":generate_webview_license_notice",
     "//third_party/icu:icu_assets",
     "//v8:v8_external_startup_data_assets",
   ]
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
new file mode 100644
index 0000000..a846f82
--- /dev/null
+++ b/android_webview/apk/BUILD.gn
@@ -0,0 +1,15 @@
+# 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("//build/config/android/rules.gni")
+
+# Since Monochrome has its own content provider, these two files are put
+# in two different targets.
+android_library("webview_license_provider_java") {
+  java_files = [ "//android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java" ]
+}
+
+android_library("webview_license_activity_java") {
+  java_files = [ "//android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java" ]
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java
similarity index 72%
rename from android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java
rename to android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java
index 7607a94..7427d0d 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java
@@ -17,18 +17,20 @@
  * other than LicenseContentProvider.
  */
 public class LicenseActivity extends Activity {
+    private static final String LICENSES_URI_SUFFIX = "LicenseContentProvider/webview_licenses";
+    private static final String LICENSES_CONTENT_TYPE = "text/html";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         final String packageName = getPackageName();
         final Intent intent = new Intent(Intent.ACTION_VIEW);
-        final String licenseUri = String.format("content://%s.%s",
-                packageName, LicenseContentProvider.LICENSES_URI_SUFFIX);
-        intent.setDataAndType(
-                Uri.parse(licenseUri), LicenseContentProvider.LICENSES_CONTENT_TYPE);
+        final String licenseUri =
+                String.format("content://%s.%s", packageName, LICENSES_URI_SUFFIX);
+        intent.setDataAndType(Uri.parse(licenseUri), LICENSES_CONTENT_TYPE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
-        final int titleId = getResources().getIdentifier(
-                "license_activity_title", "string", packageName);
+        final int titleId =
+                getResources().getIdentifier("license_activity_title", "string", packageName);
         if (titleId != 0) {
             intent.putExtra(Intent.EXTRA_TITLE, getString(titleId));
         }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
similarity index 95%
rename from android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java
rename to android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
index 0571e7cb..ef2dd2f 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
@@ -68,8 +68,7 @@
     }
 
     @Override
-    public int update(Uri uri, ContentValues values, String where,
-                      String[] whereArgs) {
+    public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
         throw new UnsupportedOperationException();
     }
 
@@ -84,8 +83,8 @@
     }
 
     @Override
-    public Cursor query(Uri uri, String[] projection, String selection,
-                        String[] selectionArgs, String sortOrder) {
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
         throw new UnsupportedOperationException();
     }
-}
+}
\ No newline at end of file
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index ab52954..b114738 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -61,8 +61,6 @@
     "java/src/com/android/webview/chromium/DrawGLFunctor.java",
     "java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java",
     "java/src/com/android/webview/chromium/GraphicsUtils.java",
-    "java/src/com/android/webview/chromium/LicenseActivity.java",
-    "java/src/com/android/webview/chromium/LicenseContentProvider.java",
     "java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java",
     "java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java",
     "java/src/com/android/webview/chromium/ServiceWorkerControllerAdapter.java",
diff --git a/ash/touch_hud/BUILD.gn b/ash/touch_hud/BUILD.gn
index 8c509ef..4411118 100644
--- a/ash/touch_hud/BUILD.gn
+++ b/ash/touch_hud/BUILD.gn
@@ -15,6 +15,7 @@
 
   deps = [
     "//base",
+    "//cc/paint",
     "//skia",
     "//ui/compositor",
     "//ui/events",
diff --git a/ash/touch_hud/touch_hud_renderer.cc b/ash/touch_hud/touch_hud_renderer.cc
index d6ce0c34..473fcae 100644
--- a/ash/touch_hud/touch_hud_renderer.cc
+++ b/ash/touch_hud/touch_hud_renderer.cc
@@ -84,7 +84,7 @@
       alpha = static_cast<int>(fadeout_->CurrentValueBetween(alpha, 0));
     fill_flags_.setAlpha(alpha);
     stroke_flags_.setAlpha(alpha);
-    fill_flags_.setShader(SkGradientShader::MakeRadial(
+    fill_flags_.setShader(cc::PaintShader::MakeRadialGradient(
         gradient_center_, SkIntToScalar(kPointRadius), gradient_colors_,
         gradient_pos_, arraysize(gradient_colors_),
         SkShader::kMirror_TileMode));
diff --git a/base/memory/shared_memory.h b/base/memory/shared_memory.h
index 61e54d8..91a8153ff 100644
--- a/base/memory/shared_memory.h
+++ b/base/memory/shared_memory.h
@@ -64,22 +64,6 @@
   bool share_read_only = false;
 };
 
-// Enumeration of different shared memory error types. Note: Currently only
-// errors from Mac POSIX shared memory implementation are fully instrumented.
-// TODO(asvitkine): Evaluate whether we want to keep this ability after
-// crbug.com/703649 is fixed and expand to all platforms or remove.
-enum class SharedMemoryError {
-  NO_ERRORS,
-  NO_FILE,
-  BAD_PARAMS,
-  STAT_FAILED,
-  TRUNCATE_FAILED,
-  NO_TEMP_DIR,
-  MAKE_READONLY_FAILED,
-  INODE_MISMATCH,
-  MMAP_FAILED,
-};
-
 // Platform abstraction for shared memory.
 // SharedMemory consumes a SharedMemoryHandle [potentially one that it created]
 // to map a shared memory OS resource into the virtual address space of the
@@ -226,13 +210,6 @@
   // failure.
   SharedMemoryHandle GetReadOnlyHandle();
 
-  // Returns the last error encountered as a result of a call to Create() or
-  // Map(). Note: Currently only errors from Mac POSIX shared memory
-  // implementation are fully instrumented.
-  // TODO(asvitkine): Evaluate whether we want to keep this ability after
-  // crbug.com/703649 is fixed and expand to all platforms or remove.
-  SharedMemoryError get_last_error() const { return last_error_; }
-
  private:
 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \
     (!defined(OS_MACOSX) || defined(OS_IOS))
@@ -263,7 +240,6 @@
   void* memory_ = nullptr;
   bool read_only_ = false;
   size_t requested_size_ = 0;
-  SharedMemoryError last_error_ = SharedMemoryError::NO_ERRORS;
 
   DISALLOW_COPY_AND_ASSIGN(SharedMemory);
 };
diff --git a/base/memory/shared_memory_helper.cc b/base/memory/shared_memory_helper.cc
index 87c3d36..d210808 100644
--- a/base/memory/shared_memory_helper.cc
+++ b/base/memory/shared_memory_helper.cc
@@ -25,8 +25,7 @@
 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
                                  ScopedFILE* fp,
                                  ScopedFD* readonly_fd,
-                                 FilePath* path,
-                                 SharedMemoryError* error) {
+                                 FilePath* path) {
 #if !(defined(OS_MACOSX) && !defined(OS_IOS))
   // It doesn't make sense to have a open-existing private piece of shmem
   DCHECK(!options.open_existing_deprecated);
@@ -35,16 +34,13 @@
   // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
   FilePath directory;
   ScopedPathUnlinker path_unlinker;
-  if (!GetShmemTempDir(options.executable, &directory)) {
-    *error = SharedMemoryError::NO_TEMP_DIR;
+  if (!GetShmemTempDir(options.executable, &directory))
     return false;
-  }
 
   fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
-  if (!*fp) {
-    *error = SharedMemoryError::NO_FILE;
+
+  if (!*fp)
     return false;
-  }
 
   // Deleting the file prevents anyone else from mapping it in (making it
   // private), and prevents the need for cleanup (once the last fd is
@@ -57,7 +53,6 @@
     if (!readonly_fd->is_valid()) {
       DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
       fp->reset();
-      *error = SharedMemoryError::MAKE_READONLY_FAILED;
       return false;
     }
   }
@@ -67,14 +62,11 @@
 bool PrepareMapFile(ScopedFILE fp,
                     ScopedFD readonly_fd,
                     int* mapped_file,
-                    int* readonly_mapped_file,
-                    SharedMemoryError* error) {
+                    int* readonly_mapped_file) {
   DCHECK_EQ(-1, *mapped_file);
   DCHECK_EQ(-1, *readonly_mapped_file);
-  if (!fp) {
-    *error = SharedMemoryError::NO_FILE;
+  if (fp == NULL)
     return false;
-  }
 
   // This function theoretically can block on the disk, but realistically
   // the temporary files we create will just go into the buffer cache
@@ -91,7 +83,6 @@
       NOTREACHED();
     if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
       LOG(ERROR) << "writable and read-only inodes don't match; bailing";
-      *error = SharedMemoryError::INODE_MISMATCH;
       return false;
     }
   }
diff --git a/base/memory/shared_memory_helper.h b/base/memory/shared_memory_helper.h
index 5c8b86e..ca681e1 100644
--- a/base/memory/shared_memory_helper.h
+++ b/base/memory/shared_memory_helper.h
@@ -16,21 +16,18 @@
 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if
 // options.share_read_only is true. |path| is populated with the location of
 // the file before it was unlinked.
-// Returns false if there's a failure and sets |error|.
+// Returns false if there's an unhandled failure.
 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
                                  ScopedFILE* fp,
                                  ScopedFD* readonly_fd,
-                                 FilePath* path,
-                                 SharedMemoryError* error);
+                                 FilePath* path);
 
 // Takes the outputs of CreateAnonymousSharedMemory and maps them properly to
 // |mapped_file| or |readonly_mapped_file|, depending on which one is populated.
-// Returns false if there's a failure and sets |error|.
 bool PrepareMapFile(ScopedFILE fp,
                     ScopedFD readonly_fd,
                     int* mapped_file,
-                    int* readonly_mapped_file,
-                    SharedMemoryError* error);
+                    int* readonly_mapped_file);
 #endif
 
 }  // namespace base
diff --git a/base/memory/shared_memory_mac.cc b/base/memory/shared_memory_mac.cc
index 9719e97..00a32d3 100644
--- a/base/memory/shared_memory_mac.cc
+++ b/base/memory/shared_memory_mac.cc
@@ -128,15 +128,11 @@
 // "name == L"". The exception is in the StatsTable.
 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
   DCHECK(!shm_.IsValid());
-  if (options.size == 0) {
-    last_error_ = SharedMemoryError::BAD_PARAMS;
+  if (options.size == 0)
     return false;
-  }
 
-  if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) {
-    last_error_ = SharedMemoryError::BAD_PARAMS;
+  if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
     return false;
-  }
 
   if (options.type == SharedMemoryHandle::MACH) {
     shm_ = SharedMemoryHandle(options.size, UnguessableToken::Create());
@@ -153,31 +149,26 @@
   ScopedFD readonly_fd;
 
   FilePath path;
-  bool result = CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path,
-                                            &last_error_);
+  bool result = CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
   if (!result)
     return false;
   DCHECK(fp);  // Should be guaranteed by CreateAnonymousSharedMemory().
 
   // Get current size.
   struct stat stat;
-  if (fstat(fileno(fp.get()), &stat) != 0) {
-    last_error_ = SharedMemoryError::STAT_FAILED;
+  if (fstat(fileno(fp.get()), &stat) != 0)
     return false;
-  }
   const size_t current_size = stat.st_size;
   if (current_size != options.size) {
-    if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0) {
-      last_error_ = SharedMemoryError::TRUNCATE_FAILED;
+    if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
       return false;
-    }
   }
   requested_size_ = options.size;
 
   int mapped_file = -1;
   int readonly_mapped_file = -1;
   result = PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file,
-                          &readonly_mapped_file, &last_error_);
+                          &readonly_mapped_file);
   shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), options.size,
                             UnguessableToken::Create());
   readonly_shm_ =
@@ -187,18 +178,12 @@
 }
 
 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
-  if (!shm_.IsValid()) {
-    last_error_ = SharedMemoryError::BAD_PARAMS;
+  if (!shm_.IsValid())
     return false;
-  }
-  if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) {
-    last_error_ = SharedMemoryError::BAD_PARAMS;
+  if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
     return false;
-  }
-  if (memory_) {
-    last_error_ = SharedMemoryError::BAD_PARAMS;
+  if (memory_)
     return false;
-  }
 
   bool success = shm_.MapAt(offset, bytes, &memory_, read_only_);
   if (success) {
@@ -208,7 +193,6 @@
     mapped_memory_mechanism_ = shm_.type_;
     SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
   } else {
-    last_error_ = SharedMemoryError::MMAP_FAILED;
     memory_ = NULL;
   }
 
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc
index b123849f..90b8aa4 100644
--- a/base/memory/shared_memory_posix.cc
+++ b/base/memory/shared_memory_posix.cc
@@ -100,8 +100,8 @@
 
   FilePath path;
   if (options.name_deprecated == NULL || options.name_deprecated->empty()) {
-    bool result = CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path,
-                                              &last_error_);
+    bool result =
+        CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
     if (!result)
       return false;
   } else {
@@ -192,9 +192,8 @@
 
   int mapped_file = -1;
   int readonly_mapped_file = -1;
-  bool result =
-      PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file,
-                     &readonly_mapped_file, &last_error_);
+  bool result = PrepareMapFile(std::move(fp), std::move(readonly_fd),
+                               &mapped_file, &readonly_mapped_file);
   shm_ = SharedMemoryHandle(base::FileDescriptor(mapped_file, false),
                             options.size, UnguessableToken::Create());
   readonly_shm_ =
@@ -234,9 +233,8 @@
   }
   int mapped_file = -1;
   int readonly_mapped_file = -1;
-  bool result =
-      PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file,
-                     &readonly_mapped_file, &last_error_);
+  bool result = PrepareMapFile(std::move(fp), std::move(readonly_fd),
+                               &mapped_file, &readonly_mapped_file);
   // This form of sharing shared memory is deprecated. https://crbug.com/345734.
   // However, we can't get rid of it without a significant refactor because its
   // used to communicate between two versions of the same service process, very
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 2d07818d8..9c2110a 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -11,7 +11,6 @@
 #include "base/build_time.h"
 #include "base/command_line.h"
 #include "base/debug/activity_tracker.h"
-#include "base/debug/crash_logging.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/process/memory.h"
@@ -1280,25 +1279,11 @@
 #endif
 
   std::unique_ptr<SharedMemory> shm(new SharedMemory());
-  if (!shm->Create(options)) {
-#if !defined(OS_NACL)
-    // Temporary for http://crbug.com/703649.
-    base::debug::ScopedCrashKey crash_key(
-        "field_trial_shmem_create_error",
-        base::IntToString(static_cast<int>(shm->get_last_error())));
-#endif
+  if (!shm->Create(options))
     OnOutOfMemory(kFieldTrialAllocationSize);
-  }
 
-  if (!shm->Map(kFieldTrialAllocationSize)) {
-#if !defined(OS_NACL)
-    // Temporary for http://crbug.com/703649.
-    base::debug::ScopedCrashKey crash_key(
-        "field_trial_shmem_map_error",
-        base::IntToString(static_cast<int>(shm->get_last_error())));
-#endif
+  if (!shm->Map(kFieldTrialAllocationSize))
     OnOutOfMemory(kFieldTrialAllocationSize);
-  }
 
   global_->field_trial_allocator_.reset(
       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
diff --git a/base/process/process.h b/base/process/process.h
index 8979dd4..9902fafd 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -139,6 +139,9 @@
   // Returns true if the priority was changed, false otherwise. If
   // |port_provider| is null, this is a no-op and it returns false.
   bool SetProcessBackgrounded(PortProvider* port_provider, bool value);
+
+  // Returns |true| if helper processes should participate in AppNap.
+  static bool IsAppNapEnabled();
 #else
   // A process is backgrounded when it's priority is lower than normal.
   // Return true if this process is backgrounded, false otherwise.
diff --git a/base/process/process_mac.cc b/base/process/process_mac.cc
index f83fbb99..bc045cd72a 100644
--- a/base/process/process_mac.cc
+++ b/base/process/process_mac.cc
@@ -8,15 +8,27 @@
 
 #include "base/feature_list.h"
 #include "base/mac/mach_logging.h"
+#include "base/metrics/field_trial_params.h"
 
 namespace base {
 
+namespace {
+const char kAppNapFeatureParamName[] = "app_nap";
+}
+
 // Enables backgrounding hidden renderers on Mac.
 const Feature kMacAllowBackgroundingProcesses{"MacAllowBackgroundingProcesses",
                                               FEATURE_DISABLED_BY_DEFAULT};
 
+bool Process::IsAppNapEnabled() {
+  return !base::GetFieldTrialParamValueByFeature(
+              kMacAllowBackgroundingProcesses, kAppNapFeatureParamName)
+              .empty();
+}
+
 bool Process::CanBackgroundProcesses() {
-  return FeatureList::IsEnabled(kMacAllowBackgroundingProcesses);
+  return FeatureList::IsEnabled(kMacAllowBackgroundingProcesses) &&
+         !IsAppNapEnabled();
 }
 
 bool Process::IsProcessBackgrounded(PortProvider* port_provider) const {
diff --git a/base/sequenced_task_runner.h b/base/sequenced_task_runner.h
index e42ba7f..fa768fb2 100644
--- a/base/sequenced_task_runner.h
+++ b/base/sequenced_task_runner.h
@@ -158,8 +158,7 @@
 // std::unique_ptr<Foo, base::OnTaskRunnerDeleter> ptr(
 //     new Foo, base::OnTaskRunnerDeleter(my_task_runner));
 //
-// TODO: RefCounted isn't yet supported per RefCountedTraits using a static
-// deleter and thus not be bindable to a specific TaskRunner.
+// For RefCounted see base::RefCountedDeleteOnSequence.
 struct BASE_EXPORT OnTaskRunnerDeleter {
   explicit OnTaskRunnerDeleter(scoped_refptr<SequencedTaskRunner> task_runner);
   ~OnTaskRunnerDeleter();
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
index 5d884e5..bd16f2b4 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -42,7 +42,7 @@
 // Cannot call ThreadIdNameManager::GetName because it holds a lock and causes
 // deadlock when lock is already held by ThreadIdNameManager before the current
 // allocation. Gets the thread name from kernel if available or returns a string
-// with id. This function intenionally leaks the allocated strings since they
+// with id. This function intentionally leaks the allocated strings since they
 // are used to tag allocations even after the thread dies.
 const char* GetAndLeakThreadName() {
   char name[16];
@@ -156,7 +156,6 @@
   task_contexts_.pop_back();
 }
 
-// static
 bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
   if (ignore_scope_depth_)
     return false;
diff --git a/base/trace_event/heap_profiler_allocation_register.h b/base/trace_event/heap_profiler_allocation_register.h
index d7e8c91..bead7b2 100644
--- a/base/trace_event/heap_profiler_allocation_register.h
+++ b/base/trace_event/heap_profiler_allocation_register.h
@@ -379,7 +379,7 @@
   //
   // This is a slightly abstraction to allow for constant propagation. It
   // knows that the sentinel will be the first item inserted into the table
-  // and that the first index retuned will be 0. The constructor DCHECKs
+  // and that the first index returned will be 0. The constructor DCHECKs
   // this assumption.
   enum : BacktraceMap::KVIndex { kOutOfStorageBacktraceIndex = 0 };
 
diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h
index 08495a7..b2487d0a 100644
--- a/base/trace_event/malloc_dump_provider.h
+++ b/base/trace_event/malloc_dump_provider.h
@@ -5,9 +5,6 @@
 #ifndef BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
 #define BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
 
-#include <istream>
-#include <memory>
-
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/threading/platform_thread.h"
@@ -53,7 +50,7 @@
 
   // When in OnMemoryDump(), this contains the current thread ID.
   // This is to prevent re-entrancy in the heap profiler when the heap dump
-  // generation is malloc/new-ing for its own bookeeping data structures.
+  // generation is malloc/new-ing for its own bookkeeping data structures.
   PlatformThreadId tid_dumping_heap_;
 
   DISALLOW_COPY_AND_ASSIGN(MallocDumpProvider);
diff --git a/base/win/com_init_util.cc b/base/win/com_init_util.cc
index a90e588..53a6d73d 100644
--- a/base/win/com_init_util.cc
+++ b/base/win/com_init_util.cc
@@ -14,12 +14,6 @@
 
 namespace {
 
-enum class ComInitStatus {
-  NONE,
-  STA,
-  MTA,
-};
-
 // Derived from combase.dll.
 struct OleTlsData {
   enum ApartmentFlags {
@@ -36,27 +30,31 @@
   // to work between x86 and x64 builds.
 };
 
-ComInitStatus GetComInitStatusForThread() {
+ComApartmentType GetComApartmentTypeForThread() {
   TEB* teb = NtCurrentTeb();
   OleTlsData* ole_tls_data = reinterpret_cast<OleTlsData*>(teb->ReservedForOle);
   if (!ole_tls_data)
-    return ComInitStatus::NONE;
+    return ComApartmentType::NONE;
 
   if (ole_tls_data->apartment_flags & OleTlsData::ApartmentFlags::STA)
-    return ComInitStatus::STA;
+    return ComApartmentType::STA;
 
   if ((ole_tls_data->apartment_flags & OleTlsData::ApartmentFlags::MTA) ==
       OleTlsData::ApartmentFlags::MTA) {
-    return ComInitStatus::MTA;
+    return ComApartmentType::MTA;
   }
 
-  return ComInitStatus::NONE;
+  return ComApartmentType::NONE;
 }
 
 }  // namespace
 
 void AssertComInitialized() {
-  DCHECK_NE(ComInitStatus::NONE, GetComInitStatusForThread());
+  DCHECK_NE(ComApartmentType::NONE, GetComApartmentTypeForThread());
+}
+
+void AssertComApartmentType(ComApartmentType apartment_type) {
+  DCHECK_EQ(apartment_type, GetComApartmentTypeForThread());
 }
 
 #endif  // DCHECK_IS_ON()
diff --git a/base/win/com_init_util.h b/base/win/com_init_util.h
index 29312893..46794e9 100644
--- a/base/win/com_init_util.h
+++ b/base/win/com_init_util.h
@@ -11,11 +11,27 @@
 namespace base {
 namespace win {
 
-// DCHECKs if COM is not initialized on this thread as an STA or MTA.
+enum class ComApartmentType {
+  // Uninitialized or has an unrecognized apartment type.
+  NONE,
+  // Single-threaded Apartment.
+  STA,
+  // Multi-threaded Apartment.
+  MTA,
+};
+
 #if DCHECK_IS_ON()
+
+// DCHECKs if COM is not initialized on this thread as an STA or MTA.
 BASE_EXPORT void AssertComInitialized();
+
+// DCHECKs if |apartment_type| is not the same as the current thread's apartment
+// type.
+BASE_EXPORT void AssertComApartmentType(ComApartmentType apartment_type);
+
 #else   // DCHECK_IS_ON()
 void AssertComInitialized() {}
+void AssertComApartmentType(ComApartmentType apartment_type) {}
 #endif  // DCHECK_IS_ON()
 
 }  // namespace win
diff --git a/base/win/com_init_util_unittest.cc b/base/win/com_init_util_unittest.cc
index 8f6f512..f5387d0 100644
--- a/base/win/com_init_util_unittest.cc
+++ b/base/win/com_init_util_unittest.cc
@@ -16,6 +16,8 @@
 }
 
 TEST(ComInitUtil, AssertUninitialized) {
+  // When COM is uninitialized, the TLS data will remain, but the apartment
+  // status will be updated. This covers that case.
   {
     ScopedCOMInitializer com_initializer;
     ASSERT_TRUE(com_initializer.succeeded());
@@ -37,5 +39,37 @@
   AssertComInitialized();
 }
 
+TEST(ComInitUtil, AssertNoneApartmentType) {
+  AssertComApartmentType(ComApartmentType::NONE);
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::STA));
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::MTA));
+}
+
+TEST(ComInitUtil, AssertNoneApartmentTypeUninitialized) {
+  // When COM is uninitialized, the TLS data will remain, but the apartment
+  // status will be updated. This covers that case.
+  {
+    ScopedCOMInitializer com_initializer;
+    ASSERT_TRUE(com_initializer.succeeded());
+  }
+  AssertComApartmentType(ComApartmentType::NONE);
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::STA));
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::MTA));
+}
+
+TEST(ComInitUtil, AssertSTAApartmentType) {
+  ScopedCOMInitializer com_initializer;
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::NONE));
+  AssertComApartmentType(ComApartmentType::STA);
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::MTA));
+}
+
+TEST(ComInitUtil, AssertMTAApartmentType) {
+  ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::NONE));
+  EXPECT_DCHECK_DEATH(AssertComApartmentType(ComApartmentType::STA));
+  AssertComApartmentType(ComApartmentType::MTA);
+}
+
 }  // namespace win
 }  // namespace base
diff --git a/build/win/merge_pgc_files.py b/build/win/merge_pgc_files.py
index dac547a0b..024bc6b 100755
--- a/build/win/merge_pgc_files.py
+++ b/build/win/merge_pgc_files.py
@@ -23,6 +23,26 @@
 import vs_toolchain
 
 
+# Number of PGC files that should be merged in each iteration, merging all
+# the files one by one is really slow but merging more than 10 at a time doesn't
+# really seem to impact the total time (when merging 180 files).
+#
+# Number of pgc merged per iteration  |  Time (in min)
+# 1                                   |  27.2
+# 10                                  |  12.8
+# 20                                  |  12.0
+# 30                                  |  11.5
+# 40                                  |  11.4
+# 50                                  |  11.5
+# 60                                  |  11.6
+# 70                                  |  11.6
+# 80                                  |  11.7
+#
+# TODO(sebmarchand): Measure the memory usage of pgomgr.exe to see how it get
+#     affected by the number of pgc files.
+_BATCH_SIZE_DEFAULT = 10
+
+
 def find_pgomgr(chrome_checkout_dir):
   """Find pgomgr.exe."""
   win_toolchain_json_file = os.path.join(chrome_checkout_dir, 'build',
@@ -49,6 +69,20 @@
   return pgomgr_path
 
 
+def merge_pgc_files(pgomgr_path, files, pgd_path):
+  """Merge all the pgc_files in |files| to |pgd_path|."""
+  merge_command = [
+      pgomgr_path,
+      '/merge'
+  ]
+  merge_command.extend(files)
+  merge_command.append(pgd_path)
+  proc = subprocess.Popen(merge_command, stdout=subprocess.PIPE)
+  stdout, _ = proc.communicate()
+  print stdout
+  return proc.returncode
+
+
 def main():
   parser = optparse.OptionParser(usage='%prog [options]')
   parser.add_option('--checkout-dir', help='The Chrome checkout directory.')
@@ -56,6 +90,9 @@
   parser.add_option('--build-dir', help='Chrome build directory.')
   parser.add_option('--binary-name', help='The binary for which the PGC files '
                     'should be merged, without extension.')
+  parser.add_option('--files-per-iter', help='The number of PGC files to merge '
+                    'in each iteration, default to %d.' % _BATCH_SIZE_DEFAULT,
+                    type='int', default=_BATCH_SIZE_DEFAULT)
   options, _ = parser.parse_args()
 
   if not options.checkout_dir:
@@ -70,25 +107,7 @@
 
   pgc_files = glob.glob(os.path.join(options.build_dir,
                                      '%s*.pgc' % options.binary_name))
-
-  # Number of PGC files that should be merged in each iterations, merging all
-  # the files one by one is really slow but merging more to 10 at a time doesn't
-  # really seem to impact the total time.
-  #
-  # Number of pgc merged per iteration  |  Time (in min)
-  # 1                                   |  27.2
-  # 10                                  |  12.8
-  # 20                                  |  12.0
-  # 30                                  |  11.5
-  # 40                                  |  11.4
-  # 50                                  |  11.5
-  # 60                                  |  11.6
-  # 70                                  |  11.6
-  # 80                                  |  11.7
-  #
-  # TODO(sebmarchand): Measure the memory usage of pgomgr.exe to see how it get
-  #     affected by the number of pgc files.
-  pgc_per_iter = 20
+  pgd_file = os.path.join(options.build_dir, '%s.pgd' % options.binary_name)
 
   def _split_in_chunks(items, chunk_size):
     """Split |items| in chunks of size |chunk_size|.
@@ -97,25 +116,29 @@
     """
     for i in xrange(0, len(items), chunk_size):
       yield items[i:i + chunk_size]
-
-  for chunk in _split_in_chunks(pgc_files, pgc_per_iter):
-    merge_command = [
-        pgomgr_path,
-        '/merge'
-    ]
+  for chunk in _split_in_chunks(pgc_files, options.files_per_iter):
+    files_to_merge = []
     for pgc_file in chunk:
-      merge_command.append([
-          os.path.join(options.build_dir, os.path.basename(pgc_file))
-      ])
-
-    merge_command.append([
-        os.path.join(options.build_dir, '%s.pgd' % options.binary_name)
-    ])
-    proc = subprocess.Popen(merge_command, stdout=subprocess.PIPE)
-    stdout, stderr = proc.communicate()
-    print stdout
-    if proc.returncode != 0:
-      raise Exception('Error while trying to merge the PGC files:\n%s' % stderr)
+      files_to_merge.append(
+          os.path.join(options.build_dir, os.path.basename(pgc_file)))
+    ret = merge_pgc_files(pgomgr_path, files_to_merge, pgd_file)
+    # pgomgr.exe sometimes fails to merge too many files at the same time (it
+    # usually complains that a stream is missing, but if you try to merge this
+    # file individually it works), try to merge all the PGCs from this batch one
+    # at a time instead. Don't fail the build if we can't merge a file.
+    # TODO(sebmarchand): Report this to Microsoft, check if this is still
+    # happening with VS2017.
+    if ret != 0:
+      print ('Error while trying to merge several PGC files at the same time, '
+             'trying to merge them one by one.'
+      for pgc_file in chunk:
+        ret = merge_pgc_files(
+            pgomgr_path,
+            [os.path.join(options.build_dir, os.path.basename(pgc_file))],
+            pgd_file
+        )
+        if ret != 0:
+          print 'Error while trying to merge %s, continuing.' % pgc_file
 
 
 if __name__ == '__main__':
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index 27d14909..f43cddd 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -303,11 +303,9 @@
     return false;
 
   gfx::DecomposedTransform to_return;
-  if (!gfx::BlendDecomposedTransforms(&to_return,
-                                      *decomposed_transform_.get(),
-                                      *from.decomposed_transform_.get(),
-                                      progress))
-    return false;
+  to_return = gfx::BlendDecomposedTransforms(*decomposed_transform_.get(),
+                                             *from.decomposed_transform_.get(),
+                                             progress);
 
   *result = ComposeTransform(to_return);
   return true;
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 8728420f..baa9458a 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -29,6 +29,7 @@
     "paint_record.h",
     "paint_recorder.cc",
     "paint_recorder.h",
+    "paint_shader.cc",
     "paint_shader.h",
     "record_paint_canvas.cc",
     "record_paint_canvas.h",
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 4f1fc48..4d8368b 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -597,8 +597,9 @@
         SkMatrix scale = SkMatrix::MakeScale(std::max(x * 0.5f, kMinScale),
                                              std::max(y * 0.5f, kMinScale));
         PaintFlags flags;
-        flags.setShader(discardable_image[y][x]->makeShader(
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &scale));
+        flags.setShader(PaintShader::MakeImage(
+            discardable_image[y][x], SkShader::kClamp_TileMode,
+            SkShader::kClamp_TileMode, &scale));
         content_layer_client.add_draw_rect(
             gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), flags);
       }
diff --git a/cc/paint/discardable_image_store.cc b/cc/paint/discardable_image_store.cc
index 1545e32a..4f635fd6 100644
--- a/cc/paint/discardable_image_store.cc
+++ b/cc/paint/discardable_image_store.cc
@@ -176,7 +176,7 @@
 // are not yet handled.
 void DiscardableImageStore::AddImageFromFlags(const SkRect& rect,
                                               const PaintFlags& flags) {
-  SkShader* shader = flags.getShader();
+  SkShader* shader = flags.getSkShader();
   if (shader) {
     SkMatrix matrix;
     SkShader::TileMode xy[2];
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc
index e16a8bb..03a5fe61 100644
--- a/cc/paint/paint_flags.cc
+++ b/cc/paint/paint_flags.cc
@@ -16,7 +16,7 @@
     return false;
   if (getPathEffect())
     return false;
-  if (getShader())
+  if (HasShader())
     return false;
   if (getMaskFilter())
     return false;
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index c884098..b8e5fb8 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "cc/paint/paint_export.h"
+#include "cc/paint/paint_shader.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
@@ -19,8 +20,6 @@
 
 namespace cc {
 
-using PaintShader = SkShader;
-
 class CC_PAINT_EXPORT PaintFlags {
  public:
   enum Style {
@@ -160,9 +159,21 @@
   ALWAYS_INLINE void setMaskFilter(sk_sp<SkMaskFilter> mask) {
     paint_.setMaskFilter(std::move(mask));
   }
-  ALWAYS_INLINE PaintShader* getShader() const { return paint_.getShader(); }
-  ALWAYS_INLINE void setShader(sk_sp<PaintShader> shader) {
-    paint_.setShader(std::move(shader));
+  // TODO(vmpstr): Remove this from recording calls, since we want to avoid
+  // constructing the shader until rasterization.
+  ALWAYS_INLINE SkShader* getSkShader() const { return paint_.getShader(); }
+
+  // Returns true if the shader has been set on the flags.
+  ALWAYS_INLINE bool HasShader() const { return !!paint_.getShader(); }
+
+  // Returns whether the shader is opaque. Note that it is only valid to call
+  // this function if HasShader() returns true.
+  ALWAYS_INLINE bool ShaderIsOpaque() const {
+    return paint_.getShader()->isOpaque();
+  }
+
+  ALWAYS_INLINE void setShader(std::unique_ptr<PaintShader> shader) {
+    paint_.setShader(shader ? shader->sk_shader() : nullptr);
   }
   ALWAYS_INLINE SkPathEffect* getPathEffect() const {
     return paint_.getPathEffect();
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index d48044b..22cc137 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -117,7 +117,7 @@
     if (!IsDrawOp())
       return false;
 
-    SkShader* shader = flags.getShader();
+    SkShader* shader = flags.getSkShader();
     SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr;
     return image && image->isLazyGenerated();
   }
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 9686955..a6bcbb7 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "cc/paint/paint_op_buffer.h"
+#include "base/memory/ptr_util.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_skcanvas.h"
@@ -447,8 +448,9 @@
   PaintOpBuffer buffer;
   PaintFlags flags;
   sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(100, 100));
-  flags.setShader(
-      image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
+  flags.setShader(PaintShader::MakeImage(std::move(image),
+                                         SkShader::kClamp_TileMode,
+                                         SkShader::kClamp_TileMode, nullptr));
   buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);
   EXPECT_TRUE(buffer.HasDiscardableImages());
 }
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
new file mode 100644
index 0000000..fb8bb25
--- /dev/null
+++ b/cc/paint/paint_shader.cc
@@ -0,0 +1,115 @@
+// 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 "cc/paint/paint_shader.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+namespace cc {
+
+std::unique_ptr<PaintShader> PaintShader::MakeColor(SkColor color) {
+  return base::WrapUnique(new PaintShader(nullptr, color));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakeLinearGradient(
+    const SkPoint points[],
+    const SkColor colors[],
+    const SkScalar pos[],
+    int count,
+    SkShader::TileMode mode,
+    uint32_t flags,
+    const SkMatrix* local_matrix,
+    SkColor fallback_color) {
+  return base::WrapUnique(
+      new PaintShader(SkGradientShader::MakeLinear(points, colors, pos, count,
+                                                   mode, flags, local_matrix),
+                      fallback_color));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakeRadialGradient(
+    const SkPoint& center,
+    SkScalar radius,
+    const SkColor colors[],
+    const SkScalar pos[],
+    int color_count,
+    SkShader::TileMode mode,
+    uint32_t flags,
+    const SkMatrix* local_matrix,
+    SkColor fallback_color) {
+  return base::WrapUnique(new PaintShader(
+      SkGradientShader::MakeRadial(center, radius, colors, pos, color_count,
+                                   mode, flags, local_matrix),
+      fallback_color));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakeTwoPointConicalGradient(
+    const SkPoint& start,
+    SkScalar start_radius,
+    const SkPoint& end,
+    SkScalar end_radius,
+    const SkColor colors[],
+    const SkScalar pos[],
+    int color_count,
+    SkShader::TileMode mode,
+    uint32_t flags,
+    const SkMatrix* local_matrix,
+    SkColor fallback_color) {
+  return base::WrapUnique(
+      new PaintShader(SkGradientShader::MakeTwoPointConical(
+                          start, start_radius, end, end_radius, colors, pos,
+                          color_count, mode, flags, local_matrix),
+                      fallback_color));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakeSweepGradient(
+    SkScalar cx,
+    SkScalar cy,
+    const SkColor colors[],
+    const SkScalar pos[],
+    int color_count,
+    uint32_t flags,
+    const SkMatrix* local_matrix,
+    SkColor fallback_color) {
+  return base::WrapUnique(new PaintShader(
+      SkGradientShader::MakeSweep(cx, cy, colors, pos, color_count, flags,
+                                  local_matrix),
+      fallback_color));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakeImage(
+    sk_sp<const SkImage> image,
+    SkShader::TileMode tx,
+    SkShader::TileMode ty,
+    const SkMatrix* local_matrix) {
+  return base::WrapUnique(new PaintShader(
+      image->makeShader(tx, ty, local_matrix), SK_ColorTRANSPARENT));
+}
+
+std::unique_ptr<PaintShader> PaintShader::MakePaintRecord(
+    sk_sp<PaintRecord> record,
+    const SkRect& tile,
+    SkShader::TileMode tx,
+    SkShader::TileMode ty,
+    const SkMatrix* local_matrix) {
+  return base::WrapUnique(new PaintShader(
+      SkShader::MakePictureShader(ToSkPicture(std::move(record), tile), tx, ty,
+                                  local_matrix, nullptr),
+      SK_ColorTRANSPARENT));
+}
+
+PaintShader::PaintShader(sk_sp<SkShader> shader, SkColor fallback_color)
+    : sk_shader_(shader ? std::move(shader)
+                        : SkShader::MakeColorShader(fallback_color)) {
+  DCHECK(sk_shader_);
+}
+PaintShader::PaintShader(const PaintShader& other) = default;
+PaintShader::PaintShader(PaintShader&& other) = default;
+PaintShader::~PaintShader() = default;
+
+PaintShader& PaintShader::operator=(const PaintShader& other) = default;
+PaintShader& PaintShader::operator=(PaintShader&& other) = default;
+
+}  // namespace cc
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 019174443b..6fc1a46 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -5,32 +5,92 @@
 #ifndef CC_PAINT_PAINT_SHADER_H_
 #define CC_PAINT_PAINT_SHADER_H_
 
-#include "cc/paint/paint_record.h"
+#include <memory>
+
+#include "cc/paint/paint_export.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkScalar.h"
 #include "third_party/skia/include/core/SkShader.h"
 
 namespace cc {
-using PaintShader = SkShader;
 
-inline sk_sp<PaintShader> WrapSkShader(sk_sp<SkShader> shader) {
-  return shader;
-}
+class PaintOpBuffer;
+using PaintRecord = PaintOpBuffer;
 
-inline sk_sp<PaintShader> MakePaintShaderImage(sk_sp<const SkImage> image,
-                                               SkShader::TileMode tx,
-                                               SkShader::TileMode ty,
-                                               const SkMatrix* local_matrix) {
-  return image->makeShader(tx, ty, local_matrix);
-}
+class CC_PAINT_EXPORT PaintShader {
+ public:
+  static std::unique_ptr<PaintShader> MakeColor(SkColor color);
 
-inline sk_sp<PaintShader> MakePaintShaderRecord(sk_sp<PaintRecord> record,
-                                                const SkRect& tile,
+  static std::unique_ptr<PaintShader> MakeLinearGradient(
+      const SkPoint points[],
+      const SkColor colors[],
+      const SkScalar pos[],
+      int count,
+      SkShader::TileMode mode,
+      uint32_t flags = 0,
+      const SkMatrix* local_matrix = nullptr,
+      SkColor fallback_color = SK_ColorTRANSPARENT);
+
+  static std::unique_ptr<PaintShader> MakeRadialGradient(
+      const SkPoint& center,
+      SkScalar radius,
+      const SkColor colors[],
+      const SkScalar pos[],
+      int color_count,
+      SkShader::TileMode mode,
+      uint32_t flags = 0,
+      const SkMatrix* local_matrix = nullptr,
+      SkColor fallback_color = SK_ColorTRANSPARENT);
+
+  static std::unique_ptr<PaintShader> MakeTwoPointConicalGradient(
+      const SkPoint& start,
+      SkScalar start_radius,
+      const SkPoint& end,
+      SkScalar end_radius,
+      const SkColor colors[],
+      const SkScalar pos[],
+      int color_count,
+      SkShader::TileMode mode,
+      uint32_t flags = 0,
+      const SkMatrix* local_matrix = nullptr,
+      SkColor fallback_color = SK_ColorTRANSPARENT);
+
+  static std::unique_ptr<PaintShader> MakeSweepGradient(
+      SkScalar cx,
+      SkScalar cy,
+      const SkColor colors[],
+      const SkScalar pos[],
+      int color_count,
+      uint32_t flags = 0,
+      const SkMatrix* local_matrix = nullptr,
+      SkColor fallback_color = SK_ColorTRANSPARENT);
+
+  static std::unique_ptr<PaintShader> MakeImage(sk_sp<const SkImage> image,
                                                 SkShader::TileMode tx,
                                                 SkShader::TileMode ty,
-                                                const SkMatrix* local_matrix) {
-  return SkShader::MakePictureShader(ToSkPicture(record, tile), tx, ty,
-                                     local_matrix, nullptr);
-}
+                                                const SkMatrix* local_matrix);
+
+  static std::unique_ptr<PaintShader> MakePaintRecord(
+      sk_sp<PaintRecord> record,
+      const SkRect& tile,
+      SkShader::TileMode tx,
+      SkShader::TileMode ty,
+      const SkMatrix* local_matrix);
+
+  PaintShader(const PaintShader& other);
+  PaintShader(PaintShader&& other);
+  ~PaintShader();
+
+  PaintShader& operator=(const PaintShader& other);
+  PaintShader& operator=(PaintShader&& other);
+
+  const sk_sp<SkShader>& sk_shader() const { return sk_shader_; }
+
+ private:
+  PaintShader(sk_sp<SkShader> shader, SkColor fallback_color);
+
+  sk_sp<SkShader> sk_shader_;
+};
 
 }  // namespace cc
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 88e42716..6bee9fc 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -494,6 +494,7 @@
     "//net/android:net_java",
     "//net/android:net_java_test_support",
     "//printing:printing_java",
+    "//services:service_javatests",
     "//third_party/WebKit/public:android_mojo_bindings_java",
     "//third_party/WebKit/public:blink_headers_java",
     "//third_party/android_protobuf:protobuf_nano_javalib",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 69980874..566de37 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -122,9 +122,11 @@
     }
     deps += [
       "//android_webview:monochrome_webview_assets",
+      "//android_webview/apk:webview_license_activity_java",
       "//android_webview/glue",
       "//chrome/android:chrome_public_non_pak_assets",
       "//chrome/android:monochrome_pak_assets",
+      "//chrome/android/monochrome:monochrome_license_provider_java",
     ]
 
     if (!is_java_debug) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
index 100e403..75850a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
@@ -95,6 +95,14 @@
                 "WebApk.Install.GooglePlayInstallResult", result, GOOGLE_PLAY_INSTALL_RESULT_MAX);
     }
 
+    /** Records the error code if installing a WebAPK via Google Play fails. */
+    public static void recordGooglePlayInstallErrorCode(int errorCode) {
+        // Don't use an enumerated histogram as there are > 30 potential error codes. In practice,
+        // a given client will always get the same error code.
+        RecordHistogram.recordSparseSlowlyHistogram(
+                "WebApk.Install.GooglePlayErrorCode", Math.min(errorCode, 1000));
+    }
+
     /**
      * Records whether updating a WebAPK from Google Play succeeded. If not, records the reason
      * that the update failed.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index cf50fb3..e1c8b93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -55,7 +55,6 @@
 
     /** Whether an offline sub-feature is enabled or not. */
     private static Boolean sOfflineBookmarksEnabled;
-    private static Boolean sBackgroundLoadingEnabled;
     private static Boolean sIsPageSharingEnabled;
 
     /**
@@ -528,6 +527,11 @@
         return nativeGetOfflinePage(mNativeOfflinePageBridge, webContents);
     }
 
+    /**
+     * Allows setting the offline bookmarks feature as enabled or disabled for testing. This is
+     * required for tests that don't load the native binary otherwise UnsatisfiedLinkError sadness
+     * will occur.
+     */
     @VisibleForTesting
     static void setOfflineBookmarksEnabledForTesting(boolean enabled) {
         sOfflineBookmarksEnabled = enabled;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
index 59bee9c..52d540f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
@@ -84,7 +84,7 @@
             getActivity().finish();
             return true;
         } else if (item.getItemId() == R.id.help_menu_id) {
-            EditorDialog.launchAutofillHelpPage(mContext);
+            EditorDialog.launchAutofillHelpPage(getActivity());
             return true;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/GooglePlayWebApkInstallDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/GooglePlayWebApkInstallDelegate.java
index 7de2c24..c19430a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/GooglePlayWebApkInstallDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/GooglePlayWebApkInstallDelegate.java
@@ -4,34 +4,13 @@
 
 package org.chromium.chrome.browser.webapps;
 
-import android.support.annotation.IntDef;
-
 import org.chromium.base.Callback;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Defines an interface for installing WebAPKs via Google Play.
  */
 public interface GooglePlayWebApkInstallDelegate {
     /**
-     * The app state transitions provided by Google Play during download and installation process.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({INVALID, DOWNLOAD_PENDING, DOWNLOADING, DOWNLOAD_CANCELLED, DOWNLOAD_ERROR,
-             INSTALLING, INSTALL_ERROR, INSTALLED})
-    public @interface InstallerPackageEvent {}
-    public static final int INVALID = -1;
-    public static final int DOWNLOAD_PENDING = 0;
-    public static final int DOWNLOADING = 1;
-    public static final int DOWNLOAD_CANCELLED = 2;
-    public static final int DOWNLOAD_ERROR = 3;
-    public static final int INSTALLING = 4;
-    public static final int INSTALL_ERROR = 5;
-    public static final int INSTALLED = 6;
-
-    /**
      * Uses Google Play to install WebAPK asynchronously.
      * @param packageName The package name of WebAPK to install.
      * @param version The version of WebAPK to install.
@@ -54,10 +33,4 @@
      */
     void updateAsync(String packageName, int version, String title, String token, String url,
             Callback<Integer> callback);
-    /**
-     * Calls the callback once the installation either succeeded or failed.
-     * @param packageName The package name of WebAPK for the installation.
-     * @param event The result of the install.
-     */
-    void onGotInstallEvent(String packageName, @InstallerPackageEvent int event);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
index c8b2e8d..ecba3522 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
@@ -84,10 +84,9 @@
     // The default shell Apk version of WebAPKs.
     static final int DEFAULT_SHELL_APK_VERSION = 1;
 
-    // Unset/invalid constants for last used times and URLs. 0 is used as the null last used time as
-    // WebappRegistry assumes that this is always a valid timestamp.
-    static final long LAST_USED_UNSET = 0;
-    static final long LAST_USED_INVALID = -1;
+    // Invalid constants for timestamps and URLs. '0' is used as the invalid timestamp as
+    // WebappRegistry and WebApkUpdateManager assume that timestamps are always valid.
+    static final long TIMESTAMP_INVALID = 0;
     static final String URL_INVALID = "";
     static final int VERSION_INVALID = 0;
 
@@ -139,14 +138,8 @@
      * Opens an instance of WebappDataStorage for the web app specified.
      * @param webappId The ID of the web app.
      */
-    static WebappDataStorage open(final String webappId) {
-        final WebappDataStorage storage = sFactory.create(webappId);
-        if (storage.getLastUsedTime() == LAST_USED_INVALID) {
-            // If the last used time is invalid then ensure that there is no data in the
-            // WebappDataStorage which needs to be cleaned up.
-            assert storage.isEmpty();
-        }
-        return storage;
+    static WebappDataStorage open(String webappId) {
+        return sFactory.create(webappId);
     }
 
     /**
@@ -321,10 +314,7 @@
     void clearHistory() {
         SharedPreferences.Editor editor = mPreferences.edit();
 
-        // The last used time is set to 0 to ensure that a valid value is always present.
-        // If the web app is not launched prior to the next cleanup, then its remaining data will be
-        // removed. Otherwise, the next launch from home screen will update the last used time.
-        editor.putLong(KEY_LAST_USED, LAST_USED_UNSET);
+        editor.remove(KEY_LAST_USED);
         editor.remove(KEY_URL);
         editor.remove(KEY_SCOPE);
         editor.remove(KEY_LAST_CHECK_WEB_MANIFEST_UPDATE_TIME);
@@ -364,7 +354,7 @@
      * Returns the last used time of this object, or -1 if it is not stored.
      */
     public long getLastUsedTime() {
-        return mPreferences.getLong(KEY_LAST_USED, LAST_USED_INVALID);
+        return mPreferences.getLong(KEY_LAST_USED, TIMESTAMP_INVALID);
     }
 
     /**
@@ -406,7 +396,7 @@
      * updated. This time needs to be set when the WebAPK is registered.
      */
     private long getLastCheckForWebManifestUpdateTime() {
-        return mPreferences.getLong(KEY_LAST_CHECK_WEB_MANIFEST_UPDATE_TIME, LAST_USED_INVALID);
+        return mPreferences.getLong(KEY_LAST_CHECK_WEB_MANIFEST_UPDATE_TIME, TIMESTAMP_INVALID);
     }
 
     /**
@@ -423,7 +413,7 @@
      * This time needs to be set when the WebAPK is registered.
      */
     long getLastWebApkUpdateRequestCompletionTime() {
-        return mPreferences.getLong(KEY_LAST_UPDATE_REQUEST_COMPLETE_TIME, LAST_USED_INVALID);
+        return mPreferences.getLong(KEY_LAST_UPDATE_REQUEST_COMPLETE_TIME, TIMESTAMP_INVALID);
     }
 
     /**
@@ -485,7 +475,7 @@
      */
     boolean didPreviousUpdateSucceed() {
         long lastUpdateCompletionTime = getLastWebApkUpdateRequestCompletionTime();
-        if (lastUpdateCompletionTime == WebappDataStorage.LAST_USED_INVALID) {
+        if (lastUpdateCompletionTime == TIMESTAMP_INVALID) {
             return true;
         }
         return getDidLastWebApkUpdateRequestSucceed();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
index 1997ce9..2880e3f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
@@ -117,8 +117,10 @@
 
             @Override
             protected final void onPostExecute(WebappDataStorage storage) {
-                // Guarantee that last used time != WebappDataStorage.LAST_USED_INVALID. Must be
-                // run on the main thread as SharedPreferences.Editor.apply() is called.
+                // Update the last used time in order to prevent
+                // {@link WebappRegistry@unregisterOldWebapps()} from deleting the
+                // WebappDataStorage. Must be run on the main thread as
+                // SharedPreferences.Editor.apply() is called.
                 mStorages.put(webappId, storage);
                 mPreferences.edit().putStringSet(KEY_WEBAPP_SET, mStorages.keySet()).apply();
                 storage.updateLastUsedTime();
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 13fcb49..c5aa7ed3 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1760,7 +1760,7 @@
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java",
-  "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java",
+  "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/TaskExtrasPackerTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridgeTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index a45433d7..c06f1b44 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -42,8 +42,7 @@
 
 /** Unit tests for {@link OfflinePageBridge}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({"enable-features=OfflineBookmarks",
-        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
         ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
 public class OfflinePageBridgeTest {
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
index da04976..bff40f0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
@@ -33,8 +33,7 @@
 
 /** Unit tests for offline page request handling. */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({"enable-features=OfflineBookmarks",
-        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
         ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
 public class OfflinePageRequestTest {
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
index fa4c93ea..3256c1b9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
@@ -41,9 +41,9 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-/** Unit tests for {@link OfflinePageUtils}. */
+/** Instrumentation tests for {@link OfflinePageUtils}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({"enable-features=OfflineBookmarks", "enable-features=OfflinePagesSharing",
+@CommandLineFlags.Add({"enable-features=OfflinePagesSharing",
         ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
         ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
 public class OfflinePageUtilsTest {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
index bd270dc..1808c63 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
@@ -39,8 +39,7 @@
 /** Integration tests for the Last 1 feature of Offline Pages. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
-        ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG,
-        "enable-features=OfflineRecentPages"})
+        ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
 public class RecentTabsTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
similarity index 96%
rename from chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
index 96a0747..32e440b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
@@ -42,8 +42,8 @@
  */
 @RunWith(LocalRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, application = BaseChromiumApplication.class,
-        shadows = {OfflinePageUtilsTest.WrappedEnvironment.class, ShadowMultiDex.class})
-public class OfflinePageUtilsTest {
+        shadows = {OfflinePageUtilsUnitTest.WrappedEnvironment.class, ShadowMultiDex.class})
+public class OfflinePageUtilsUnitTest {
     @Mock
     private File mMockDataDirectory;
     @Mock
@@ -78,6 +78,8 @@
                 .getOfflinePageBridge((Profile) isNull());
         OfflinePageUtils.setInstanceForTesting(mOfflinePageUtils);
 
+        // Setting the default value is required because unit tests don't load native code needed
+        // to normally call the respective getter.
         OfflinePageBridge.setOfflineBookmarksEnabledForTesting(true);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 6b36fd8..693f4ebb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -71,7 +71,7 @@
 
     /** {@link WebappDataStorage#Clock} subclass which enables time to be manually advanced. */
     private static class MockClock extends WebappDataStorage.Clock {
-        // 0 has a special meaning: {@link WebappDataStorage#LAST_USED_UNSET}.
+        // 0 has a special meaning: {@link WebappDataStorage#TIMESTAMP_INVALID}.
         private long mTimeMillis = 1;
 
         public void advance(long millis) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
index 757e15c..89b2c24 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
@@ -18,7 +18,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
@@ -194,11 +193,10 @@
         storage.updateLastUsedTime();
         assertTrue(storage.wasLaunchedRecently());
 
-        long lastUsedTime = mSharedPreferences.getLong(WebappDataStorage.KEY_LAST_USED,
-                WebappDataStorage.LAST_USED_INVALID);
+        long lastUsedTime = mSharedPreferences.getLong(
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
 
-        assertTrue(lastUsedTime != WebappDataStorage.LAST_USED_UNSET);
-        assertTrue(lastUsedTime != WebappDataStorage.LAST_USED_INVALID);
+        assertTrue(lastUsedTime != WebappDataStorage.TIMESTAMP_INVALID);
 
         // Move the last used time one day in the past.
         mSharedPreferences.edit()
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
index 1dc4165..f52d40af 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
@@ -17,7 +17,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
@@ -127,8 +126,8 @@
         long after = System.currentTimeMillis();
         SharedPreferences webAppPrefs = ContextUtils.getApplicationContext().getSharedPreferences(
                 WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "test", Context.MODE_PRIVATE);
-        long actual = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
-                WebappDataStorage.LAST_USED_INVALID);
+        long actual = webAppPrefs.getLong(
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
         assertTrue("Timestamp is out of range", actual <= after);
     }
 
@@ -247,8 +246,8 @@
         Set<String> actual = getRegisteredWebapps();
         assertEquals(new HashSet<>(Arrays.asList("oldWebapp")), actual);
 
-        long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
-                WebappDataStorage.LAST_USED_INVALID);
+        long actualLastUsed = webAppPrefs.getLong(
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
         assertEquals(Long.MIN_VALUE, actualLastUsed);
 
         // The last cleanup time was set to 0 in setUp() so check that this hasn't changed.
@@ -280,8 +279,8 @@
         Set<String> actual = getRegisteredWebapps();
         assertEquals(new HashSet<>(Arrays.asList("recentWebapp")), actual);
 
-        long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
-                WebappDataStorage.LAST_USED_INVALID);
+        long actualLastUsed = webAppPrefs.getLong(
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
         assertEquals(lastUsed, actualLastUsed);
 
         long lastCleanup = mSharedPreferences.getLong(WebappRegistry.KEY_LAST_CLEANUP, -1);
@@ -311,9 +310,9 @@
         Set<String> actual = getRegisteredWebapps();
         assertTrue(actual.isEmpty());
 
-        long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
-                WebappDataStorage.LAST_USED_INVALID);
-        assertEquals(WebappDataStorage.LAST_USED_INVALID, actualLastUsed);
+        long actualLastUsed = webAppPrefs.getLong(
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertEquals(WebappDataStorage.TIMESTAMP_INVALID, actualLastUsed);
 
         long lastCleanup = mSharedPreferences.getLong(WebappRegistry.KEY_LAST_CLEANUP, -1);
         assertEquals(currentTime, lastCleanup);
@@ -421,11 +420,11 @@
                 WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "webapp2", Context.MODE_PRIVATE);
 
         long webapp1OriginalLastUsed = webapp2Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
         long webapp2OriginalLastUsed = webapp2Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
-        assertTrue(webapp1OriginalLastUsed != WebappDataStorage.LAST_USED_UNSET);
-        assertTrue(webapp2OriginalLastUsed != WebappDataStorage.LAST_USED_UNSET);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertTrue(webapp1OriginalLastUsed != WebappDataStorage.TIMESTAMP_INVALID);
+        assertTrue(webapp2OriginalLastUsed != WebappDataStorage.TIMESTAMP_INVALID);
 
         // Clear data for |webapp1Url|.
         WebappRegistry.getInstance().clearWebappHistoryForUrlsImpl(
@@ -437,12 +436,12 @@
         assertTrue(actual.contains("webapp2"));
 
         // Verify that the last used time for the first web app is
-        // WebappDataStorage.LAST_USED_UNSET, while for the second one it's unchanged.
+        // WebappDataStorage.TIMESTAMP_INVALID, while for the second one it's unchanged.
         long actualLastUsed = webapp1Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
-        assertEquals(WebappDataStorage.LAST_USED_UNSET, actualLastUsed);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertEquals(WebappDataStorage.TIMESTAMP_INVALID, actualLastUsed);
         actualLastUsed = webapp2Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
         assertEquals(webapp2OriginalLastUsed, actualLastUsed);
 
         // Verify that the URL and scope for the first web app is WebappDataStorage.URL_INVALID,
@@ -463,13 +462,13 @@
         // Clear data for all urls.
         WebappRegistry.getInstance().clearWebappHistoryForUrlsImpl(new UrlFilters.AllUrls());
 
-        // Verify that the last used time for both web apps is WebappDataStorage.LAST_USED_UNSET.
+        // Verify that the last used time for both web apps is WebappDataStorage.TIMESTAMP_INVALID.
         actualLastUsed = webapp1Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
-        assertEquals(WebappDataStorage.LAST_USED_UNSET, actualLastUsed);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertEquals(WebappDataStorage.TIMESTAMP_INVALID, actualLastUsed);
         actualLastUsed = webapp2Prefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
-        assertEquals(WebappDataStorage.LAST_USED_UNSET, actualLastUsed);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertEquals(WebappDataStorage.TIMESTAMP_INVALID, actualLastUsed);
 
         // Verify that the URL and scope for both web apps is WebappDataStorage.URL_INVALID.
         actualScope = webapp1Prefs.getString(
@@ -500,9 +499,8 @@
 
         // Verify that the last used time is valid.
         long actualLastUsed = webappPrefs.getLong(
-                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_INVALID);
-        assertTrue(WebappDataStorage.LAST_USED_INVALID != actualLastUsed);
-        assertTrue(WebappDataStorage.LAST_USED_UNSET != actualLastUsed);
+                WebappDataStorage.KEY_LAST_USED, WebappDataStorage.TIMESTAMP_INVALID);
+        assertTrue(WebappDataStorage.TIMESTAMP_INVALID != actualLastUsed);
     }
 
     @Test
diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn
new file mode 100644
index 0000000..2553c5d
--- /dev/null
+++ b/chrome/android/monochrome/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+android_library("monochrome_license_provider_java") {
+  java_files =
+      [ "java/src/com/android/webview/chromium/LicenseContentProvider.java" ]
+
+  deps = [
+    "//base:base_java",
+    "//chrome/android:chrome_java",
+    "//components/about_ui/android:aboutui_java",
+  ]
+}
diff --git a/chrome/android/monochrome/java/DEPS b/chrome/android/monochrome/java/DEPS
new file mode 100644
index 0000000..69fc978
--- /dev/null
+++ b/chrome/android/monochrome/java/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/about_ui/android/java",
+]
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java b/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java
similarity index 70%
copy from android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java
copy to chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java
index 0571e7cb..5bc5c683 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java
+++ b/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java
@@ -14,15 +14,18 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.components.aboutui.CreditUtils;
+
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 
 /**
  * Content provider for the OSS licenses file.
- * This is compiled into the stub WebView and so should not depend on any classes from Chromium.
  */
 @TargetApi(Build.VERSION_CODES.KITKAT)
 public class LicenseContentProvider
@@ -47,15 +50,21 @@
     @Override
     public void writeDataToPipe(
             ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, String filename) {
-        try (InputStream in = getContext().getAssets().open(filename);
-                OutputStream out = new FileOutputStream(output.getFileDescriptor());) {
-            byte[] buf = new byte[8192];
-            int size = -1;
-            while ((size = in.read(buf)) != -1) {
-                out.write(buf, 0, size);
-            }
+        try (OutputStream out = new FileOutputStream(output.getFileDescriptor());) {
+            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        ChromeBrowserInitializer.getInstance(getContext())
+                                .handleSynchronousStartup();
+                    } catch (ProcessInitException e) {
+                        Log.e(TAG, "Fail to initialize the Chrome Browser.", e);
+                    }
+                }
+            });
+            out.write(CreditUtils.nativeGetJavaWrapperCredits());
         } catch (IOException e) {
-            Log.e(TAG, "Failed to read the license file", e);
+            Log.e(TAG, "Failed to write the license file", e);
         }
     }
 
@@ -68,8 +77,7 @@
     }
 
     @Override
-    public int update(Uri uri, ContentValues values, String where,
-                      String[] whereArgs) {
+    public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
         throw new UnsupportedOperationException();
     }
 
@@ -84,8 +92,8 @@
     }
 
     @Override
-    public Cursor query(Uri uri, String[] projection, String selection,
-                        String[] selectionArgs, String sortOrder) {
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index 23ed8d8..6963b31b 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -182,10 +182,6 @@
       {"engine_params", crash_keys::kMediumSize},
       {"engine1_params", crash_keys::kMediumSize},
       {"engine2_params", crash_keys::kMediumSize},
-
-      // Temporary for http://crbug.com/703649.
-      {"field_trial_shmem_create_error", crash_keys::kSmallSize},
-      {"field_trial_shmem_map_error", crash_keys::kSmallSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/app/helper-Info.plist b/chrome/app/helper-Info.plist
index cd9991a2..5fca138 100644
--- a/chrome/app/helper-Info.plist
+++ b/chrome/app/helper-Info.plist
@@ -26,5 +26,7 @@
 	<string>1</string>
 	<key>NSSupportsAutomaticGraphicsSwitching</key>
 	<true/>
+	<key>NSSupportsAppNap</key>
+	<true/>
 </dict>
 </plist>
diff --git a/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc b/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
index 12bd237d..ab47895 100644
--- a/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
+++ b/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
@@ -127,11 +127,6 @@
 OfflinePageUtilsTest::~OfflinePageUtilsTest() {}
 
 void OfflinePageUtilsTest::SetUp() {
-  // Enables offline pages feature.
-  // TODO(jianli): Remove this once the feature is completely enabled.
-  scoped_feature_list_.InitAndEnableFeature(
-      offline_pages::kOfflineBookmarksFeature);
-
   // Create a test web contents.
   web_contents_.reset(content::WebContents::Create(
       content::WebContents::CreateParams(profile())));
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
index 3e4c535..5f66503 100644
--- a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
+++ b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
@@ -156,7 +156,6 @@
   size_t model_removed_count_;
   std::vector<OfflinePageItem> all_pages_;
   bool all_pages_needs_updating_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 
   // Mocks the RenderViewHostTestHarness' main thread runner. Needs to be delay
   // initialized in SetUp() -- can't be a simple member -- since
@@ -209,7 +208,6 @@
   mocked_main_runner_ =
       base::MakeUnique<base::ScopedMockTimeMessageLoopTaskRunner>();
 
-  scoped_feature_list_.InitAndEnableFeature(kOffliningRecentPagesFeature);
   // Sets up the factories for testing.
   OfflinePageModelFactory::GetInstance()->SetTestingFactoryAndUse(
       browser_context(), BuildTestOfflinePageModel);
@@ -655,7 +653,7 @@
 // Download requests should still work.
 TEST_F(RecentTabHelperTest, LastNFeatureNotEnabled) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.Init();
+  scoped_feature_list.InitAndDisableFeature(kOffliningRecentPagesFeature);
   NavigateAndCommit(kTestPageUrl);
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
   FastForwardSnapshotController();
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 0b7c25f..3296458 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -60,6 +60,8 @@
     "ui_elements/button.h",
     "ui_elements/exit_prompt.cc",
     "ui_elements/exit_prompt.h",
+    "ui_elements/exit_prompt_backplane.cc",
+    "ui_elements/exit_prompt_backplane.h",
     "ui_elements/exit_warning.cc",
     "ui_elements/exit_warning.h",
     "ui_elements/loading_indicator.cc",
diff --git a/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.cc b/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.cc
new file mode 100644
index 0000000..6dcd5a8
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.cc
@@ -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.
+
+#include "chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace vr_shell {
+
+ExitPromptBackplane::ExitPromptBackplane(
+    const base::Callback<void()>& click_callback)
+    : click_callback_(click_callback) {}
+
+ExitPromptBackplane::~ExitPromptBackplane() = default;
+
+void ExitPromptBackplane::OnButtonUp(const gfx::PointF& position) {
+  // We always run the callback. That is, if the user clicks the controller
+  // button on the backplane and releases it on another element in front of the
+  // backplane, this callback will still be called. Elements are input locked
+  // on button down and it's probably not worth special-casing the backplane.
+  click_callback_.Run();
+}
+
+}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h b/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h
new file mode 100644
index 0000000..8b9f7cb
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_EXIT_PROMPT_BACKPLANE_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_EXIT_PROMPT_BACKPLANE_H_
+
+#include "base/callback.h"
+#include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
+
+namespace vr_shell {
+
+// An invisible but hittable plane behind the exit prompt, to keep the reticle
+// roughly planar with the prompt when its near the prompt.
+class ExitPromptBackplane : public UiElement {
+ public:
+  explicit ExitPromptBackplane(const base::Callback<void()>& click_callback);
+  ~ExitPromptBackplane() override;
+
+  void OnButtonUp(const gfx::PointF& position) override;
+
+ private:
+  base::Callback<void()> click_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExitPromptBackplane);
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_EXIT_PROMPT_BACKPLANE_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
index c186768..7a2add7 100644
--- a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
@@ -30,7 +30,6 @@
   if (enabled_ == enabled)
     return;
   enabled_ = enabled;
-  ResetVisibilityTimer();
   SetVisibility();
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h b/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
index e43db65..c0042fc 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
@@ -26,6 +26,7 @@
   kScreenDimmer,
   kExitWarning,
   kExitPrompt,
+  kExitPromptBackplane,
 };
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index f3f9026b..476ebf8 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/button.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_prompt.h"
+#include "chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_warning.h"
 #include "chrome/browser/android/vr_shell/ui_elements/loading_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/permanent_security_warning.h"
@@ -55,6 +56,7 @@
 static constexpr float kExitPromptWidth = 0.672 * kContentDistance;
 static constexpr float kExitPromptHeight = 0.2 * kContentDistance;
 static constexpr float kExitPromptVerticalOffset = -0.09 * kContentDistance;
+static constexpr float kExitPromptBackplaneSize = 1000.0;
 
 static constexpr float kUrlBarDistance = 2.4;
 static constexpr float kUrlBarWidth = 0.672 * kUrlBarDistance;
@@ -105,6 +107,7 @@
   CreateUrlBar();
   CreateCloseButton();
   CreateScreenDimmer();
+  CreateExitPrompt();
 
   ConfigureScene();
   ConfigureSecurityWarnings();
@@ -229,26 +232,9 @@
   element->set_size({kBackplaneSize, kBackplaneSize, 1.0});
   element->set_translation({0.0, 0.0, -kTextureOffset});
   element->set_parent_id(main_content_->id());
-  main_content_backplane_ = element.get();
   content_elements_.push_back(element.get());
   scene_->AddUiElement(std::move(element));
 
-  element = base::MakeUnique<ExitPrompt>(
-      512,
-      base::Bind(&UiSceneManager::OnExitPromptPrimaryButtonClicked,
-                 base::Unretained(this)),
-      base::Bind(&UiSceneManager::OnExitPromptSecondaryButtonClicked,
-                 base::Unretained(this)));
-  element->set_debug_id(kExitPrompt);
-  element->set_id(AllocateId());
-  element->set_fill(vr_shell::Fill::NONE);
-  element->set_size({kExitPromptWidth, kExitPromptHeight, 1});
-  element->set_translation({0.0, kExitPromptVerticalOffset, kTextureOffset});
-  element->set_parent_id(main_content_->id());
-  element->set_visible(false);
-  exit_prompt_ = element.get();
-  scene_->AddUiElement(std::move(element));
-
   // Limit reticle distance to a sphere based on content distance.
   scene_->SetBackgroundDistance(main_content_->translation().z() *
                                 -kBackgroundDistanceMultiplier);
@@ -332,6 +318,38 @@
   scene_->AddUiElement(std::move(element));
 }
 
+void UiSceneManager::CreateExitPrompt() {
+  std::unique_ptr<UiElement> element = base::MakeUnique<ExitPrompt>(
+      512,
+      base::Bind(&UiSceneManager::OnExitPromptPrimaryButtonClicked,
+                 base::Unretained(this)),
+      base::Bind(&UiSceneManager::OnExitPromptSecondaryButtonClicked,
+                 base::Unretained(this)));
+  element->set_debug_id(kExitPrompt);
+  element->set_id(AllocateId());
+  element->set_fill(vr_shell::Fill::NONE);
+  element->set_size({kExitPromptWidth, kExitPromptHeight, 1});
+  element->set_translation({0.0, kExitPromptVerticalOffset, kTextureOffset});
+  element->set_parent_id(main_content_->id());
+  element->set_visible(false);
+  exit_prompt_ = element.get();
+  scene_->AddUiElement(std::move(element));
+
+  // Place an invisible but hittable plane behind the exit prompt, to keep the
+  // reticle roughly planar with the content if near content.
+  element = base::MakeUnique<ExitPromptBackplane>(base::Bind(
+      &UiSceneManager::OnExitPromptBackplaneClicked, base::Unretained(this)));
+  element->set_debug_id(kExitPromptBackplane);
+  element->set_id(AllocateId());
+  element->set_fill(vr_shell::Fill::NONE);
+  element->set_size({kExitPromptBackplaneSize, kExitPromptBackplaneSize, 1.0});
+  element->set_translation({0.0, 0.0, -kTextureOffset});
+  element->set_parent_id(exit_prompt_->id());
+  exit_prompt_backplane_ = element.get();
+  content_elements_.push_back(element.get());
+  scene_->AddUiElement(std::move(element));
+}
+
 base::WeakPtr<UiSceneManager> UiSceneManager::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
@@ -349,7 +367,6 @@
 
 void UiSceneManager::ConfigureScene() {
   exit_warning_->SetEnabled(scene_->is_exiting());
-  exit_prompt_->SetEnabled(scene_->is_prompting_to_exit());
   screen_dimmer_->SetEnabled(scene_->is_exiting());
 
   // Controls (URL bar, loading progress, etc).
@@ -363,13 +380,20 @@
   close_button_->SetEnabled(!web_vr_mode_ && (fullscreen_ || in_cct_));
 
   // Content elements.
-  main_content_->SetEnabled(!web_vr_mode_ && !scene_->is_prompting_to_exit());
-  main_content_backplane_->SetEnabled(!web_vr_mode_);
+  for (UiElement* element : content_elements_) {
+    element->SetEnabled(!web_vr_mode_ && !scene_->is_prompting_to_exit());
+  }
+
   // Background elements.
   for (UiElement* element : background_elements_) {
     element->SetEnabled(!web_vr_mode_);
   }
 
+  // Exit prompt.
+  bool showExitPrompt = !web_vr_mode_ && scene_->is_prompting_to_exit();
+  exit_prompt_->SetEnabled(showExitPrompt);
+  exit_prompt_backplane_->SetEnabled(showExitPrompt);
+
   // Update content quad parameters depending on fullscreen.
   // TODO(http://crbug.com/642937): Animate fullscreen transitions.
   if (fullscreen_) {
@@ -475,6 +499,14 @@
   browser_->NavigateBack();
 }
 
+void UiSceneManager::OnSecurityIconClickedForTesting() {
+  OnSecurityIconClicked();
+}
+
+void UiSceneManager::OnExitPromptPrimaryButtonClickedForTesting() {
+  OnExitPromptPrimaryButtonClicked();
+}
+
 void UiSceneManager::OnSecurityIconClicked() {
   if (scene_->is_prompting_to_exit())
     return;
@@ -482,13 +514,21 @@
   ConfigureScene();
 }
 
-void UiSceneManager::OnExitPromptPrimaryButtonClicked() {
+void UiSceneManager::OnExitPromptBackplaneClicked() {
+  CloseExitPrompt();
+}
+
+void UiSceneManager::CloseExitPrompt() {
   if (!scene_->is_prompting_to_exit())
     return;
   scene_->set_is_prompting_to_exit(false);
   ConfigureScene();
 }
 
+void UiSceneManager::OnExitPromptPrimaryButtonClicked() {
+  CloseExitPrompt();
+}
+
 void UiSceneManager::OnExitPromptSecondaryButtonClicked() {
   OnUnsupportedMode(UiUnsupportedMode::kUnhandledPageInfo);
 }
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h
index c870835b..94630e3 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.h
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -52,9 +52,10 @@
   void OnAppButtonClicked();
   void OnAppButtonGesturePerformed(UiInterface::Direction direction);
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(UiSceneManagerTest, UiUpdatesExitPrompt);
+  void OnSecurityIconClickedForTesting();
+  void OnExitPromptPrimaryButtonClickedForTesting();
 
+ private:
   void CreateScreenDimmer();
   void CreateSecurityWarnings();
   void CreateSystemIndicators();
@@ -62,15 +63,18 @@
   void CreateBackground();
   void CreateUrlBar();
   void CreateCloseButton();
+  void CreateExitPrompt();
 
   void ConfigureScene();
   void ConfigureSecurityWarnings();
   void UpdateBackgroundColor();
+  void CloseExitPrompt();
   void OnSecurityWarningTimer();
   void OnBackButtonClicked();
   void OnSecurityIconClicked();
   void OnExitPromptPrimaryButtonClicked();
   void OnExitPromptSecondaryButtonClicked();
+  void OnExitPromptBackplaneClicked();
   void OnCloseButtonClicked();
   void OnUnsupportedMode(UiUnsupportedMode mode);
   int AllocateId();
@@ -84,9 +88,9 @@
   UiElement* permanent_security_warning_ = nullptr;
   UiElement* transient_security_warning_ = nullptr;
   UiElement* exit_prompt_ = nullptr;
+  UiElement* exit_prompt_backplane_ = nullptr;
   UiElement* exit_warning_ = nullptr;
   UiElement* main_content_ = nullptr;
-  UiElement* main_content_backplane_ = nullptr;
   UiElement* audio_capture_indicator_ = nullptr;
   UiElement* video_capture_indicator_ = nullptr;
   UiElement* screen_capture_indicator_ = nullptr;
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
index 5744214..a58041d 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
@@ -34,7 +34,9 @@
 };
 
 std::set<UiElementDebugId> kElementsVisibleInBrowsing = {
-    kContentQuad, kBackplane, kCeiling, kFloor, kUrlBar, kLoadingIndicator};
+    kContentQuad, kBackplane, kCeiling, kFloor, kUrlBar};
+std::set<UiElementDebugId> kElementsVisibleWithExitPrompt = {
+    kExitPrompt, kExitPromptBackplane, kCeiling, kFloor};
 
 }  // namespace
 
@@ -64,6 +66,17 @@
     return element ? element->visible() : false;
   }
 
+  void VerifyElementsVisible(const std::string& debug_name,
+                             const std::set<UiElementDebugId>& elements) {
+    SCOPED_TRACE(debug_name);
+    for (const auto& element : scene_->GetUiElements()) {
+      SCOPED_TRACE(element->debug_id());
+      bool should_be_visible =
+          elements.find(element->debug_id()) != elements.end();
+      EXPECT_EQ(should_be_visible, element->visible());
+    }
+  }
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<MockBrowserInterface> browser_;
   std::unique_ptr<UiScene> scene_;
@@ -203,43 +216,21 @@
 
   // Hold onto the background color to make sure it changes.
   SkColor initial_background = scene_->GetWorldBackgroundColor();
+  VerifyElementsVisible("Initial", kElementsVisibleInBrowsing);
 
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible =
-        kElementsVisibleInBrowsing.find(element->debug_id()) !=
-        kElementsVisibleInBrowsing.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
-
-  // Transistion to fullscreen.
+  // In fullscreen mode, content elements should be visible, control elements
+  // should be hidden.
   manager_->SetFullscreen(true);
-
-  // Content elements should be visible, control elements should be hidden.
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible = visible_in_fullscreen.find(element->debug_id()) !=
-                             visible_in_fullscreen.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
-
+  VerifyElementsVisible("In fullscreen", visible_in_fullscreen);
   {
     SCOPED_TRACE("Entered Fullsceen");
     // Make sure background has changed for fullscreen.
     EXPECT_NE(initial_background, scene_->GetWorldBackgroundColor());
   }
 
-  // Exit fullscreen.
-  manager_->SetFullscreen(false);
-
   // Everything should return to original state after leaving fullscreen.
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible =
-        kElementsVisibleInBrowsing.find(element->debug_id()) !=
-        kElementsVisibleInBrowsing.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
+  manager_->SetFullscreen(false);
+  VerifyElementsVisible("Restore initial", kElementsVisibleInBrowsing);
   {
     SCOPED_TRACE("Exited Fullsceen");
     EXPECT_EQ(initial_background, scene_->GetWorldBackgroundColor());
@@ -247,39 +238,38 @@
 }
 
 TEST_F(UiSceneManagerTest, UiUpdatesExitPrompt) {
-  std::set<UiElementDebugId> visible_when_prompting = {kExitPrompt, kBackplane,
-                                                       kCeiling, kFloor};
   MakeManager(kNotInCct, kNotInWebVr);
 
   manager_->SetWebVrSecureOrigin(true);
 
   // Initial state.
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible =
-        kElementsVisibleInBrowsing.find(element->debug_id()) !=
-        kElementsVisibleInBrowsing.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
+  VerifyElementsVisible("Initial", kElementsVisibleInBrowsing);
 
   // Exit prompt visible state.
-  manager_->OnSecurityIconClicked();
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible = visible_when_prompting.find(element->debug_id()) !=
-                             visible_when_prompting.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
+  manager_->OnSecurityIconClickedForTesting();
+  VerifyElementsVisible("Prompt visible", kElementsVisibleWithExitPrompt);
 
   // Back to initial state.
-  manager_->OnExitPromptPrimaryButtonClicked();
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    bool should_be_visible =
-        kElementsVisibleInBrowsing.find(element->debug_id()) !=
-        kElementsVisibleInBrowsing.end();
-    EXPECT_EQ(should_be_visible, element->visible());
-  }
+  manager_->OnExitPromptPrimaryButtonClickedForTesting();
+  VerifyElementsVisible("Restore initial", kElementsVisibleInBrowsing);
+}
+
+TEST_F(UiSceneManagerTest, BackplaneClickClosesExitPrompt) {
+  MakeManager(kNotInCct, kNotInWebVr);
+
+  manager_->SetWebVrSecureOrigin(true);
+
+  // Initial state.
+  VerifyElementsVisible("Initial", kElementsVisibleInBrowsing);
+
+  // Exit prompt visible state.
+  manager_->OnSecurityIconClickedForTesting();
+  VerifyElementsVisible("Prompt visble", kElementsVisibleWithExitPrompt);
+
+  // Back to initial state.
+  scene_->GetUiElementByDebugId(kExitPromptBackplane)
+      ->OnButtonUp(gfx::PointF());
+  VerifyElementsVisible("Restore initial", kElementsVisibleInBrowsing);
 }
 
 TEST_F(UiSceneManagerTest, UiUpdatesForWebVR) {
@@ -291,10 +281,7 @@
   manager_->SetScreenCapturingIndicator(true);
 
   // All elements should be hidden.
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    EXPECT_FALSE(element->visible());
-  }
+  VerifyElementsVisible("Elements hidden", std::set<UiElementDebugId>{});
 }
 
 TEST_F(UiSceneManagerTest, UiUpdateTransitionToWebVR) {
@@ -308,10 +295,7 @@
   manager_->SetWebVrSecureOrigin(true);
 
   // All elements should be hidden.
-  for (const auto& element : scene_->GetUiElements()) {
-    SCOPED_TRACE(element->debug_id());
-    EXPECT_FALSE(element->visible());
-  }
+  VerifyElementsVisible("Elements hidden", std::set<UiElementDebugId>{});
 }
 
 TEST_F(UiSceneManagerTest, CaptureIndicatorsVisibility) {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 7e3eb5f0..031d14c8 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1262,6 +1262,7 @@
     "printing/printer_configurer.h",
     "printing/printer_discoverer.cc",
     "printing/printer_discoverer.h",
+    "printing/printer_info.h",
     "printing/printers_manager.cc",
     "printing/printers_manager.h",
     "printing/printers_manager_factory.cc",
@@ -1502,11 +1503,13 @@
     sources += [
       "printing/cups_print_job_manager_impl.cc",
       "printing/cups_print_job_manager_impl.h",
+      "printing/printer_info_cups.cc",
     ]
   } else {
     sources += [
       "printing/fake_cups_print_job_manager.cc",
       "printing/fake_cups_print_job_manager.h",
+      "printing/printer_info_stub.cc",
     ]
   }
 
diff --git a/chrome/browser/chromeos/printing/printer_info.h b/chrome/browser/chromeos/printing/printer_info.h
new file mode 100644
index 0000000..f295ef52
--- /dev/null
+++ b/chrome/browser/chromeos/printing/printer_info.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_INFO_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_INFO_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace chromeos {
+
+// Callback for basic printer information.  |success| indicates if the request
+// succeeded at all.  |make| represents the printer manufacturer.  |model| is
+// the printer model.  |autoconf| indicates if we think we can compute the
+// printer capabilites without a PPD.
+using PrinterInfoCallback = base::Callback<void(bool success,
+                                                const std::string& make,
+                                                const std::string& model,
+                                                bool autoconf)>;
+
+// Dispatch an IPP request to |host| on |port| for |path| to obtain
+// basic printer information.
+void QueryIppPrinter(const std::string& host,
+                     const int port,
+                     const std::string& path,
+                     const PrinterInfoCallback& callback);
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_INFO_H_
diff --git a/chrome/browser/chromeos/printing/printer_info_cups.cc b/chrome/browser/chromeos/printing/printer_info_cups.cc
new file mode 100644
index 0000000..e11db5adc
--- /dev/null
+++ b/chrome/browser/chromeos/printing/printer_info_cups.cc
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/printer_info.h"
+
+#include <algorithm>
+#include <array>
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/version.h"
+#include "printing/backend/cups_jobs.h"
+
+namespace {
+
+const char kPdfMimeType[] = "application/pdf";
+const char kPwgRasterMimeType[] = "image/pwg-raster";
+
+// List of known multi-word printer manufacturers to help with make-and-model
+// string parsing.  Keep in UPPER CASE as that's how matches are performed.
+const std::array<const char* const, 4> kMultiWordManufacturers{
+    {"FUJI XEROX", "KODAK FUNAI", "KONICA MINOLTA", "TEXAS INSTRUMENTS"}};
+
+// Returns the length of the portion of |make_and_model| representing the
+// manufacturer.  This is either a value from kMultiWordManufacaturers or the
+// first token.  If there is only one token or less, we assume that it does not
+// represent the manufacturer and return 0.
+size_t ManufacturerLength(base::StringPiece make_and_model) {
+  // TODO(crbug.com/729245): Update when better data is available.
+  for (base::StringPiece multi_word_manufacturer : kMultiWordManufacturers) {
+    if (base::StartsWith(make_and_model, multi_word_manufacturer,
+                         base::CompareCase::INSENSITIVE_ASCII)) {
+      return multi_word_manufacturer.size();
+    }
+  }
+
+  // The position of the first space is equal to the length of the first token.
+  size_t first_space = make_and_model.find(" ");
+  return first_space != base::StringPiece::npos ? first_space : 0;
+}
+
+// Returns true if any of the |ipp_versions| are greater than or equal to 2.0.
+bool AllowedIpp(const std::vector<base::Version>& ipp_versions) {
+  auto found =
+      std::find_if(ipp_versions.begin(), ipp_versions.end(),
+                   [](const base::Version& version) {
+                     return version.IsValid() && version.components()[0] >= 2;
+                   });
+
+  return found != ipp_versions.end();
+}
+
+// Returns true if |mime_type| is one of the supported types.
+bool SupportedMime(const std::string& mime_type) {
+  return mime_type == kPwgRasterMimeType || mime_type == kPdfMimeType;
+}
+
+// Returns true if |formats| contains one of the supported printer description
+// languages for an autoconf printer identified by MIME type.
+bool SupportsRequiredPDLS(const std::vector<std::string>& formats) {
+  auto found = std::find_if(formats.begin(), formats.end(), &SupportedMime);
+  return found != formats.end();
+}
+
+// Returns true if |info| describes a printer for which we want to attempt
+// automatic configuration.
+bool IsAutoconf(const ::printing::PrinterInfo& info) {
+  return info.ipp_everywhere || (AllowedIpp(info.ipp_versions) &&
+                                 SupportsRequiredPDLS(info.document_formats));
+}
+
+// Dispatches an IPP request to |host| to retrieve printer information.  Returns
+// a nullptr if the request fails.
+std::unique_ptr<::printing::PrinterInfo> QueryPrinterImpl(
+    const std::string& host,
+    const int port,
+    const std::string& path) {
+  auto info = base::MakeUnique<::printing::PrinterInfo>();
+  if (!::printing::GetPrinterInfo(host, port, path, info.get())) {
+    LOG(ERROR) << "Could not retrieve printer info";
+    return nullptr;
+  }
+
+  return info;
+}
+
+// Handles the request for |info|.  Parses make and model information before
+// calling |callback|.
+void OnPrinterQueried(const chromeos::PrinterInfoCallback& callback,
+                      std::unique_ptr<::printing::PrinterInfo> info) {
+  if (!info) {
+    VLOG(1) << "Could not reach printer";
+    callback.Run(false, std::string(), std::string(), false);
+    return;
+  }
+
+  base::StringPiece make_and_model(info->make_and_model);
+  base::StringPiece make;
+  base::StringPiece model;
+
+  size_t split = ManufacturerLength(make_and_model);
+  if (split != 0) {
+    make = make_and_model.substr(0, split);
+    model = make_and_model.substr(split + 1);
+  } else {
+    // If there's only one word or an empty string, use it.
+    model = make_and_model;
+  }
+
+  callback.Run(true, make.as_string(), model.as_string(), IsAutoconf(*info));
+}
+
+}  // namespace
+
+namespace chromeos {
+
+void QueryIppPrinter(const std::string& host,
+                     const int port,
+                     const std::string& path,
+                     const PrinterInfoCallback& callback) {
+  DCHECK(!host.empty());
+
+  // QueryPrinterImpl could block on a network call for a noticable amount of
+  // time (100s of ms). Also the user is waiting on this result.  Thus, run at
+  // USER_VISIBLE with MayBlock.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      base::TaskTraits(base::TaskPriority::USER_VISIBLE, base::MayBlock()),
+      base::Bind(&QueryPrinterImpl, host, port, path),
+      base::Bind(&OnPrinterQueried, callback));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_info_stub.cc b/chrome/browser/chromeos/printing/printer_info_stub.cc
new file mode 100644
index 0000000..fd12c1d7
--- /dev/null
+++ b/chrome/browser/chromeos/printing/printer_info_stub.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/printer_info.h"
+
+#include "base/logging.h"
+#include "base/task_scheduler/post_task.h"
+
+namespace chromeos {
+
+void QueryIppPrinter(const std::string& host,
+                     const int port,
+                     const std::string& path,
+                     const PrinterInfoCallback& callback) {
+  DCHECK(!host.empty());
+
+  base::PostTask(FROM_HERE, base::Bind(callback, false, "Foo", "Bar", false));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
index d7897db..f7df5f54 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
+++ b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
@@ -51,9 +51,9 @@
 const int kMaxDeviceTouchEventLogs = 7;
 
 // Clean up intermediate log files dumped during feedback creation.
-void CleanupEventLog(std::unique_ptr<std::vector<base::FilePath>> log_paths) {
-  for (size_t i = 0; i < log_paths->size(); ++i)
-    base::DeleteFile((*log_paths)[i], false);
+void CleanupEventLog(const std::vector<base::FilePath>& log_paths) {
+  for (const base::FilePath& path : log_paths)
+    base::DeleteFile(path, false);
 }
 
 // Check for all known log paths and find the ones whose filenames match a
@@ -85,7 +85,7 @@
 // Pack the collected event logs in a way that is compatible with the log
 // analysis tools.
 void PackEventLog(system_logs::SystemLogsResponse* response,
-                  std::unique_ptr<std::vector<base::FilePath>> log_paths) {
+                  const std::vector<base::FilePath>& log_paths) {
   // Combine logs with a command line call that tars them up and uuencode the
   // result in one string. This is to be compatible with the X11 behavior.
   std::vector<std::pair<std::string, base::CommandLine>> commands;
@@ -94,9 +94,9 @@
 
   // Make a list that contains touchpad (and mouse) event logs only.
   const std::string touchpad_log_list =
-      GetEventLogListOfOnePrefix(*log_paths, kTouchpadGestureLogPrefix,
+      GetEventLogListOfOnePrefix(log_paths, kTouchpadGestureLogPrefix,
                                  kMaxDeviceTouchEventLogs) +
-      GetEventLogListOfOnePrefix(*log_paths, kTouchpadEvdevLogPrefix,
+      GetEventLogListOfOnePrefix(log_paths, kTouchpadEvdevLogPrefix,
                                  kMaxDeviceTouchEventLogs);
   command.AppendArg(std::string(kTarCommand) + touchpad_log_list +
                     " 2>/dev/null | " + kUuencodeCommand +
@@ -110,7 +110,7 @@
 
   // Make a list that contains touchscreen event logs only.
   const std::string touchscreen_log_list = GetEventLogListOfOnePrefix(
-      *log_paths, kTouchscreenLogPrefix, kMaxDeviceTouchEventLogs);
+      log_paths, kTouchscreenLogPrefix, kMaxDeviceTouchEventLogs);
   ts_command.AppendArg(std::string(kTarCommand) + touchscreen_log_list +
                        " 2>/dev/null | " + kUuencodeCommand +
                        " -m touchscreen_activity_log.tar");
@@ -125,9 +125,9 @@
   }
 
   // Cleanup these temporary log files.
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-      base::Bind(CleanupEventLog, base::Passed(&log_paths)));
+  base::PostTaskWithTraits(FROM_HERE,
+                           {base::MayBlock(), base::TaskPriority::BACKGROUND},
+                           base::Bind(CleanupEventLog, log_paths));
 }
 
 // Callback for handing the outcome of GetTouchEventLog().
@@ -136,14 +136,13 @@
 void OnEventLogCollected(
     std::unique_ptr<system_logs::SystemLogsResponse> response,
     const system_logs::SysLogsSourceCallback& callback,
-    std::unique_ptr<std::vector<base::FilePath>> log_paths) {
+    const std::vector<base::FilePath>& log_paths) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // We cannot eliminate these temporaries and inline these closures because the
   // compiler may call release() before get().
   const base::Closure pack_closure =
-      base::Bind(&PackEventLog, base::Unretained(response.get()),
-                 base::Passed(&log_paths));
+      base::Bind(&PackEventLog, base::Unretained(response.get()), log_paths);
   const base::Closure callback_closure =
       base::Bind(callback, base::Owned(response.release()));
   base::PostTaskWithTraitsAndReply(
@@ -158,15 +157,15 @@
 void OnStatusLogCollected(
     std::unique_ptr<system_logs::SystemLogsResponse> response,
     const system_logs::SysLogsSourceCallback& callback,
-    std::unique_ptr<std::string> log) {
+    const std::string& log) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  (*response)[kDeviceStatusLogDataKey] = *log;
+  (*response)[kDeviceStatusLogDataKey] = log;
 
   // Collect touch event logs.
   const base::FilePath kBaseLogPath(kTouchEventLogDir);
   ui::OzonePlatform::GetInstance()->GetInputController()->GetTouchEventLog(
       kBaseLogPath,
-      base::Bind(&OnEventLogCollected, base::Passed(&response), callback));
+      base::BindOnce(&OnEventLogCollected, base::Passed(&response), callback));
 }
 
 // Collect touch HUD debug logs. This needs to be done on the UI thread.
@@ -195,7 +194,7 @@
 
   // Collect touch device status logs.
   ui::OzonePlatform::GetInstance()->GetInputController()->GetTouchDeviceStatus(
-      base::Bind(&OnStatusLogCollected, base::Passed(&response), callback));
+      base::BindOnce(&OnStatusLogCollected, base::Passed(&response), callback));
 }
 
 }  // namespace system_logs
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
index 4b87365..220ccf1 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -142,22 +142,20 @@
     tab_list = std::move(media_lists[2]);
     picker_ = g_picker_factory->CreatePicker();
   } else {
+    webrtc::DesktopCaptureOptions capture_options =
+        webrtc::DesktopCaptureOptions::CreateDefault();
+    capture_options.set_disable_effects(false);
+
     // Create a screens list.
     if (show_screens) {
 #if defined(USE_ASH)
       screen_list =
           base::MakeUnique<DesktopMediaListAsh>(DesktopMediaListAsh::SCREENS);
-#endif
-      if (!screen_list) {
-        webrtc::DesktopCaptureOptions options =
-            webrtc::DesktopCaptureOptions::CreateDefault();
-        options.set_disable_effects(false);
-        std::unique_ptr<webrtc::DesktopCapturer> screen_capturer(
-            webrtc::DesktopCapturer::CreateScreenCapturer(options));
-
-        screen_list = base::MakeUnique<NativeDesktopMediaList>(
-            std::move(screen_capturer), nullptr);
-      }
+#else   // !defined(USE_ASH)
+      screen_list = base::MakeUnique<NativeDesktopMediaList>(
+          content::DesktopMediaID::TYPE_SCREEN,
+          webrtc::DesktopCapturer::CreateScreenCapturer(capture_options));
+#endif  // !defined(USE_ASH)
     }
 
     // Create a windows list.
@@ -165,17 +163,11 @@
 #if defined(USE_ASH)
       window_list =
           base::MakeUnique<DesktopMediaListAsh>(DesktopMediaListAsh::WINDOWS);
-#endif
-      if (!window_list) {
-        webrtc::DesktopCaptureOptions options =
-            webrtc::DesktopCaptureOptions::CreateDefault();
-        options.set_disable_effects(false);
-        std::unique_ptr<webrtc::DesktopCapturer> window_capturer(
-            webrtc::DesktopCapturer::CreateWindowCapturer(options));
-
-        window_list = base::MakeUnique<NativeDesktopMediaList>(
-            nullptr, std::move(window_capturer));
-      }
+#else   // !defined(USE_ASH)
+      window_list = base::MakeUnique<NativeDesktopMediaList>(
+          content::DesktopMediaID::TYPE_WINDOW,
+          webrtc::DesktopCapturer::CreateWindowCapturer(capture_options));
+#endif  // !defined(USE_ASH)
     }
 
     if (show_tabs)
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 7993b3a..689edf09 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/browser/profiles/profile.h"
@@ -47,7 +46,6 @@
 #include "extensions/test/result_catcher.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
 #include "net/test/test_data_directory.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -87,7 +85,7 @@
  private:
   content::NotificationRegistrar registrar_;
 
-  DISALLOW_COPY_AND_ASSIGN(CancelLoginDialog);
+ DISALLOW_COPY_AND_ASSIGN(CancelLoginDialog);
 };
 
 // Sends an XHR request to the provided host, port, and path, and responds when
@@ -956,198 +954,4 @@
   }
 }
 
-// Tests that the webRequest events aren't dispatched when the request initiator
-// is protected by policy.
-IN_PROC_BROWSER_TEST_F(ExtensionApiTestWithManagementPolicy,
-                       InitiatorProtectedByPolicy) {
-  // We expect that no request will be hidden or modification blocked. This
-  // means that the request to example.com will be seen by the extension.
-  {
-    ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddRuntimeBlockedHost("*", "*://notexample.com");
-  }
-
-  ASSERT_TRUE(StartEmbeddedTestServer());
-
-  // Host navigated to.
-  const std::string example_com = "example.com";
-
-  // URL of a page that initiates a cross domain requests when navigated to.
-  const GURL extension_test_url = embedded_test_server()->GetURL(
-      example_com,
-      "/extensions/api_test/webrequest/policy_blocked/ref_remote_js.html");
-
-  LoadExtension(test_data_dir_.AppendASCII("webrequest/policy_blocked"));
-
-  // Extension communicates back using this listener name.
-  const std::string listener_message = "protected_origin";
-
-  // Listen to verify extension sees the web request.
-  ExtensionTestMessageListener before_request_listener(listener_message, false);
-
-  // Wait until all remote Javascript files have been blocked / pulled down.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), extension_test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
-  // Domain that hosts javascript file referenced by example_com.
-  const std::string example2_com = "example2.com";
-
-  // The server saw a request for the remote Javascript file.
-  EXPECT_TRUE(BrowsedTo(example2_com));
-
-  // The request was seen by the extension.
-  EXPECT_TRUE(before_request_listener.was_satisfied());
-
-  // Clear the list of domains the server has seen.
-  ClearRequestLog();
-
-  // Make sure we've cleared the embedded server history.
-  EXPECT_FALSE(BrowsedTo(example2_com));
-
-  // Set the policy to hide requests to example.com or any resource
-  // it includes. We expect that in this test, the request to example2.com
-  // will not be seen by the extension.
-  {
-    ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddRuntimeBlockedHost("*", "*://" + example_com);
-  }
-
-  // Listen in case extension sees the requst.
-  ExtensionTestMessageListener before_request_listener2(listener_message,
-                                                        false);
-
-  // Wait until all remote Javascript files have been pulled down.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), extension_test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
-  // The server saw a request for the remote Javascript file.
-  EXPECT_TRUE(BrowsedTo(example2_com));
-
-  // The request was hidden from the extension.
-  EXPECT_FALSE(before_request_listener2.was_satisfied());
-}
-
-// Tests that the webRequest events aren't dispatched when the URL of the
-// request is protected by policy.
-IN_PROC_BROWSER_TEST_F(ExtensionApiTestWithManagementPolicy,
-                       UrlProtectedByPolicy) {
-  // Host protected by policy.
-  const std::string protected_domain = "example.com";
-
-  {
-    ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddRuntimeBlockedHost("*", "*://" + protected_domain);
-  }
-
-  ASSERT_TRUE(StartEmbeddedTestServer());
-
-  LoadExtension(test_data_dir_.AppendASCII("webrequest/policy_blocked"));
-
-  // Listen in case extension sees the requst.
-  ExtensionTestMessageListener before_request_listener("protected_url", false);
-
-  // Path to resolve during test navigations.
-  const std::string test_path = "/defaultresponse?protected_url";
-
-  // Navigate to the protected domain and wait until page fully loads.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), embedded_test_server()->GetURL(protected_domain, test_path),
-      WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
-  // The server saw a request for the protected site.
-  EXPECT_TRUE(BrowsedTo(protected_domain));
-
-  // The request was hidden from the extension.
-  EXPECT_FALSE(before_request_listener.was_satisfied());
-
-  // Host not protected by policy.
-  const std::string unprotected_domain = "notblockedexample.com";
-
-  // Now we'll test browsing to a non-protected website where we expect the
-  // extension to see the request.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), embedded_test_server()->GetURL(unprotected_domain, test_path),
-      WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
-  // The server saw a request for the non-protected site.
-  EXPECT_TRUE(BrowsedTo(unprotected_domain));
-
-  // The request was visible from the extension.
-  EXPECT_TRUE(before_request_listener.was_satisfied());
-}
-
-// Test that no webRequest events are seen for a protected host during normal
-// navigation. This replicates most of the tests from
-// WebRequestWithWithheldPermissions with a protected host. Granting a tab
-// specific permission shouldn't bypass our policy.
-IN_PROC_BROWSER_TEST_F(ExtensionApiTestWithManagementPolicy,
-                       WebRequestProtectedByPolicy) {
-  FeatureSwitch::ScopedOverride enable_scripts_require_action(
-      FeatureSwitch::scripts_require_action(), true);
-
-  // Host protected by policy.
-  const std::string protected_domain = "example.com";
-
-  {
-    ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddRuntimeBlockedHost("*", "*://" + protected_domain);
-  }
-
-  ASSERT_TRUE(StartEmbeddedTestServer());
-
-  ExtensionTestMessageListener listener("ready", false);
-  const Extension* extension =
-      LoadExtension(test_data_dir_.AppendASCII("webrequest_activetab"));
-  ASSERT_TRUE(extension) << message_;
-  EXPECT_TRUE(listener.WaitUntilSatisfied());
-
-  // Navigate the browser to a page in a new tab.
-  GURL url = embedded_test_server()->GetURL(protected_domain, "/empty.html");
-  chrome::NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
-  params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
-  ui_test_utils::NavigateToURL(&params);
-
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(web_contents);
-  ExtensionActionRunner* runner =
-      ExtensionActionRunner::GetForWebContents(web_contents);
-  ASSERT_TRUE(runner);
-
-  int port = embedded_test_server()->port();
-  const std::string kXhrPath = "simple.html";
-
-  // The extension shouldn't have currently received any webRequest events,
-  // since it doesn't have permission (and shouldn't receive any from an XHR).
-  EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile()));
-  PerformXhrInFrame(web_contents->GetMainFrame(), protected_domain, port,
-                    kXhrPath);
-  EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile()));
-
-  // Grant activeTab permission, and perform another XHR. The extension should
-  // still be blocked due to ExtensionSettings policy on example.com.
-  // Only records ACCESS_WITHHELD, not ACCESS_DENIED, this is why it matches
-  // BLOCKED_ACTION_NONE.
-  EXPECT_EQ(BLOCKED_ACTION_NONE, runner->GetBlockedActions(extension));
-  runner->set_default_bubble_close_action_for_testing(
-      base::WrapUnique(new ToolbarActionsBarBubbleDelegate::CloseAction(
-          ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE)));
-  runner->RunAction(extension, true);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
-  EXPECT_EQ(BLOCKED_ACTION_NONE, runner->GetBlockedActions(extension));
-  int xhr_count = GetWebRequestCountFromBackgroundPage(extension, profile());
-  // ... which means that we should have a non-zero xhr count if the policy
-  // didn't block the events.
-  EXPECT_EQ(0, xhr_count);
-  // And the extension should also block future events.
-  PerformXhrInFrame(web_contents->GetMainFrame(), protected_domain, port,
-                    kXhrPath);
-  EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile()));
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
index 0303a49..ed19ffd 100644
--- a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
@@ -181,35 +181,32 @@
       context.CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
                             NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
 
-  EXPECT_EQ(
-      PermissionsData::ACCESS_ALLOWED,
-      WebRequestPermissions::CanExtensionAccessURL(
-          extension_info_map_.get(), permissionless_extension_->id(),
-          request->url(),
-          -1,     // No tab id.
-          false,  // crosses_incognito
-          WebRequestPermissions::DO_NOT_CHECK_HOST, request->initiator()));
+  EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
+            WebRequestPermissions::CanExtensionAccessURL(
+                extension_info_map_.get(), permissionless_extension_->id(),
+                request->url(),
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::DO_NOT_CHECK_HOST));
   EXPECT_EQ(PermissionsData::ACCESS_DENIED,
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), permissionless_extension_->id(),
                 request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-                request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_HOST_PERMISSION));
   EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), com_extension_->id(), request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-                request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_HOST_PERMISSION));
   EXPECT_EQ(PermissionsData::ACCESS_DENIED,
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), com_extension_->id(), request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_ALL_URLS, request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_ALL_URLS));
 
   // Public Sessions tests.
 #if defined(OS_CHROMEOS)
@@ -221,10 +218,9 @@
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), com_policy_extension_->id(),
                 org_request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-                org_request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_HOST_PERMISSION));
 
   chromeos::ScopedTestPublicSessionLoginState login_state;
 
@@ -234,19 +230,17 @@
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), com_policy_extension_->id(),
                 org_request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-                org_request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_HOST_PERMISSION));
 
-  EXPECT_EQ(
-      PermissionsData::ACCESS_ALLOWED,
-      WebRequestPermissions::CanExtensionAccessURL(
-          extension_info_map_.get(), com_policy_extension_->id(),
-          org_request->url(),
-          -1,     // No tab id.
-          false,  // crosses_incognito
-          WebRequestPermissions::REQUIRE_ALL_URLS, org_request->initiator()));
+  EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
+            WebRequestPermissions::CanExtensionAccessURL(
+                extension_info_map_.get(), com_policy_extension_->id(),
+                org_request->url(),
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_ALL_URLS));
 
   // Make sure that chrome:// URLs cannot be accessed.
   std::unique_ptr<net::URLRequest> chrome_request(
@@ -257,9 +251,8 @@
             WebRequestPermissions::CanExtensionAccessURL(
                 extension_info_map_.get(), com_policy_extension_->id(),
                 chrome_request->url(),
-                -1,     // No tab id.
-                false,  // crosses_incognito
-                WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-                chrome_request->initiator()));
+                -1,  // No tab id.
+                false, // crosses_incognito
+                WebRequestPermissions::REQUIRE_HOST_PERMISSION));
 #endif
 }
diff --git a/chrome/browser/extensions/extension_with_management_policy_apitest.cc b/chrome/browser/extensions/extension_with_management_policy_apitest.cc
index 3b77f73d..3cce777 100644
--- a/chrome/browser/extensions/extension_with_management_policy_apitest.cc
+++ b/chrome/browser/extensions/extension_with_management_policy_apitest.cc
@@ -14,9 +14,6 @@
 
 void ExtensionApiTestWithManagementPolicy::SetUpInProcessBrowserTestFixture() {
   ExtensionApiTest::SetUpInProcessBrowserTestFixture();
-  embedded_test_server()->RegisterRequestMonitor(
-      base::Bind(&ExtensionApiTestWithManagementPolicy::MonitorRequestHandler,
-                 base::Unretained(this)));
   EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
       .WillRepeatedly(testing::Return(true));
   policy_provider_.SetAutoRefresh();
@@ -28,26 +25,3 @@
   ExtensionApiTest::SetUpOnMainThread();
   host_resolver()->AddRule("*", "127.0.0.1");
 }
-
-void ExtensionApiTestWithManagementPolicy::MonitorRequestHandler(
-    const net::test_server::HttpRequest& request) {
-  auto host = request.headers.find("Host");
-  if (host != request.headers.end()) {
-    ManagementPolicyRequestLog log;
-    size_t delimiter_pos = host->second.find(":");
-    log.host = host->second.substr(0, delimiter_pos);
-    request_log_.push_back(log);
-  }
-}
-
-bool ExtensionApiTestWithManagementPolicy::BrowsedTo(
-    const std::string& test_host) {
-  return std::find_if(request_log_.begin(), request_log_.end(),
-                      [test_host](const ManagementPolicyRequestLog& log) {
-                        return log.host == test_host;
-                      }) != request_log_.end();
-}
-
-void ExtensionApiTestWithManagementPolicy::ClearRequestLog() {
-  request_log_.clear();
-}
diff --git a/chrome/browser/extensions/extension_with_management_policy_apitest.h b/chrome/browser/extensions/extension_with_management_policy_apitest.h
index e57aff4..ef11374d 100644
--- a/chrome/browser/extensions/extension_with_management_policy_apitest.h
+++ b/chrome/browser/extensions/extension_with_management_policy_apitest.h
@@ -9,14 +9,8 @@
 #include <vector>
 
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/extensions/extension_management_test_util.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 
-struct ManagementPolicyRequestLog {
-  std::string all_headers;
-  std::string host;
-};
-
 // The ExtensionSettings policy affects host permissions which impacts several
 // API integration tests. This class enables easy declaration of
 // ExtensionSettings policies and functions commonly used during these tests.
@@ -29,12 +23,6 @@
 
  protected:
   policy::MockConfigurationPolicyProvider policy_provider_;
-  bool BrowsedTo(const std::string& test_host);
-  void ClearRequestLog();
-  void MonitorRequestHandler(const net::test_server::HttpRequest& request);
-
- private:
-  std::vector<ManagementPolicyRequestLog> request_log_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionApiTestWithManagementPolicy);
 };
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc
index bcf2eac..7206873 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.cc
@@ -83,8 +83,8 @@
     : public webrtc::DesktopCapturer::Callback {
  public:
   Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
-         std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
-         std::unique_ptr<webrtc::DesktopCapturer> window_capturer);
+         DesktopMediaID::Type type,
+         std::unique_ptr<webrtc::DesktopCapturer> capturer);
   ~Worker() override;
 
   void Refresh(const DesktopMediaID::Id& view_dialog_id);
@@ -101,8 +101,8 @@
 
   base::WeakPtr<NativeDesktopMediaList> media_list_;
 
-  std::unique_ptr<webrtc::DesktopCapturer> screen_capturer_;
-  std::unique_ptr<webrtc::DesktopCapturer> window_capturer_;
+  DesktopMediaID::Type type_;
+  std::unique_ptr<webrtc::DesktopCapturer> capturer_;
 
   std::unique_ptr<webrtc::DesktopFrame> current_frame_;
 
@@ -113,64 +113,57 @@
 
 NativeDesktopMediaList::Worker::Worker(
     base::WeakPtr<NativeDesktopMediaList> media_list,
-    std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
-    std::unique_ptr<webrtc::DesktopCapturer> window_capturer)
-    : media_list_(media_list),
-      screen_capturer_(std::move(screen_capturer)),
-      window_capturer_(std::move(window_capturer)) {
-  if (screen_capturer_)
-    screen_capturer_->Start(this);
-  if (window_capturer_)
-    window_capturer_->Start(this);
+    DesktopMediaID::Type type,
+    std::unique_ptr<webrtc::DesktopCapturer> capturer)
+    : media_list_(media_list), type_(type), capturer_(std::move(capturer)) {
+  capturer_->Start(this);
 }
 
 NativeDesktopMediaList::Worker::~Worker() {}
 
 void NativeDesktopMediaList::Worker::Refresh(
     const DesktopMediaID::Id& view_dialog_id) {
-  std::vector<SourceDescription> sources;
+  std::vector<SourceDescription> result;
 
-  if (screen_capturer_) {
-    webrtc::DesktopCapturer::SourceList screens;
-    if (screen_capturer_->GetSourceList(&screens)) {
-      bool mutiple_screens = screens.size() > 1;
-      base::string16 title;
-      for (size_t i = 0; i < screens.size(); ++i) {
-        if (mutiple_screens) {
-          // Just in case 'Screen' is inflected depending on the screen number,
-          // use plural formatter.
-          title = l10n_util::GetPluralStringFUTF16(
-              IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
-              static_cast<int>(i + 1));
-        } else {
-          title = l10n_util::GetStringUTF16(
-              IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
-        }
-        sources.push_back(SourceDescription(DesktopMediaID(
-            DesktopMediaID::TYPE_SCREEN, screens[i].id), title));
-      }
-    }
+  webrtc::DesktopCapturer::SourceList sources;
+  if (!capturer_->GetSourceList(&sources)) {
+    // Will pass empty results list to RefreshForAuraWindows().
+    sources.clear();
   }
 
-  if (window_capturer_) {
-    webrtc::DesktopCapturer::SourceList windows;
-    if (window_capturer_->GetSourceList(&windows)) {
-      for (auto it = windows.begin(); it != windows.end(); ++it) {
-        // Skip the picker dialog window.
-        if (it->id == view_dialog_id)
-          continue;
+  bool mutiple_sources = sources.size() > 1;
+  base::string16 title;
+  for (size_t i = 0; i < sources.size(); ++i) {
+    switch (type_) {
+      case DesktopMediaID::TYPE_SCREEN:
+        // Just in case 'Screen' is inflected depending on the screen number,
+        // use plural formatter.
+        title = mutiple_sources
+                    ? l10n_util::GetPluralStringFUTF16(
+                          IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
+                          static_cast<int>(i + 1))
+                    : l10n_util::GetStringUTF16(
+                          IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
+        break;
 
-        DesktopMediaID media_id(DesktopMediaID::TYPE_WINDOW, it->id);
-        sources.push_back(
-            SourceDescription(media_id, base::UTF8ToUTF16(it->title)));
-      }
+      case DesktopMediaID::TYPE_WINDOW:
+        // Skip the picker dialog window.
+        if (sources[i].id == view_dialog_id)
+          continue;
+        title = base::UTF8ToUTF16(sources[i].title);
+        break;
+
+      default:
+        NOTREACHED();
     }
+    result.push_back(
+        SourceDescription(DesktopMediaID(type_, sources[i].id), title));
   }
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::BindOnce(&NativeDesktopMediaList::RefreshForAuraWindows,
-                     media_list_, sources));
+                     media_list_, result));
 }
 
 void NativeDesktopMediaList::Worker::RefreshThumbnails(
@@ -180,22 +173,9 @@
 
   // Get a thumbnail for each native source.
   for (const auto& id : native_ids) {
-    switch (id.type) {
-      case DesktopMediaID::TYPE_SCREEN:
-        if (!screen_capturer_->SelectSource(id.id))
-          continue;
-        screen_capturer_->CaptureFrame();
-        break;
-
-      case DesktopMediaID::TYPE_WINDOW:
-        if (!window_capturer_->SelectSource(id.id))
-          continue;
-        window_capturer_->CaptureFrame();
-        break;
-
-      default:
-        NOTREACHED();
-    }
+    if (!capturer_->SelectSource(id.id))
+      continue;
+    capturer_->CaptureFrame();
 
     // Expect that DesktopCapturer to always captures frames synchronously.
     // |current_frame_| may be NULL if capture failed (e.g. because window has
@@ -232,17 +212,16 @@
 }
 
 NativeDesktopMediaList::NativeDesktopMediaList(
-    std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
-    std::unique_ptr<webrtc::DesktopCapturer> window_capturer)
+    DesktopMediaID::Type type,
+    std::unique_ptr<webrtc::DesktopCapturer> capturer)
     : DesktopMediaListBase(
           base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
       weak_factory_(this) {
   capture_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
       {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
 
-  worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
-                           std::move(screen_capturer),
-                           std::move(window_capturer)));
+  worker_.reset(
+      new Worker(weak_factory_.GetWeakPtr(), type, std::move(capturer)));
 }
 
 NativeDesktopMediaList::~NativeDesktopMediaList() {
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.h b/chrome/browser/media/webrtc/native_desktop_media_list.h
index dabab11d..08ebbb78 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list.h
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.h
@@ -21,12 +21,8 @@
 // native windows.
 class NativeDesktopMediaList : public DesktopMediaListBase {
  public:
-  // Caller may pass NULL for either of the arguments in case when only some
-  // types of sources the model should be populated with (e.g. it will only
-  // contain windows, if |screen_capturer| is NULL).
-  NativeDesktopMediaList(
-      std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
-      std::unique_ptr<webrtc::DesktopCapturer> window_capturer);
+  NativeDesktopMediaList(content::DesktopMediaID::Type type,
+                         std::unique_ptr<webrtc::DesktopCapturer> capturer);
   ~NativeDesktopMediaList() override;
 
  private:
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc b/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
index 42467a5a..8cd3fae 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
@@ -177,24 +177,6 @@
     ViewsTestBase::TearDown();
   }
 
-  void CreateWithCapturers(bool screen, bool window) {
-    webrtc::DesktopCapturer* screen_capturer = nullptr;
-    if (screen)
-      screen_capturer = new FakeScreenCapturer();
-
-    if (window)
-      window_capturer_ = new FakeWindowCapturer();
-    else
-      window_capturer_ = nullptr;
-
-    model_.reset(new NativeDesktopMediaList(
-        std::unique_ptr<webrtc::DesktopCapturer>(screen_capturer),
-        std::unique_ptr<webrtc::DesktopCapturer>(window_capturer_)));
-
-    // Set update period to reduce the time it takes to run tests.
-    model_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(20));
-  }
-
   void AddNativeWindow(int id) {
     webrtc::DesktopCapturer::Source window;
     window.id = id;
@@ -265,27 +247,19 @@
 
 #endif  // defined(USE_AURA)
 
-  void AddWindowsAndVerify(bool screen,
-                           size_t window_count,
-                           size_t aura_count,
-                           bool has_view_dialog) {
-    // Create model_.
-    CreateWithCapturers(screen, window_count > 0);
+  void AddWindowsAndVerify(bool has_view_dialog) {
+    window_capturer_ = new FakeWindowCapturer();
+    model_ = base::MakeUnique<NativeDesktopMediaList>(
+        DesktopMediaID::TYPE_WINDOW, base::WrapUnique(window_capturer_));
 
-#if !defined(USE_AURA)
-    aura_count = 0;
-#endif
-    if (aura_count >= window_count)
-      aura_count = window_count - 1;
-
-    if (window_count == 0)
-      has_view_dialog = false;
+    // Set update period to reduce the time it takes to run tests.
+    model_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(20));
 
     // Set up widows.
-    size_t aura_window_first_index = window_count - aura_count;
-    for (size_t i = 0; i < window_count; ++i) {
+    size_t aura_window_first_index = kDefaultWindowCount - kDefaultAuraCount;
+    for (size_t i = 0; i < kDefaultWindowCount; ++i) {
       if (i < aura_window_first_index) {
-        AddNativeWindow(i + 1);
+        AddNativeWindow(i);
       } else {
 #if defined(USE_AURA)
         AddAuraWindow();
@@ -296,6 +270,8 @@
     if (window_capturer_)
       window_capturer_->SetWindowList(window_list_);
 
+    size_t window_count = kDefaultWindowCount;
+
     // Set view dialog window ID as the first window id.
     if (has_view_dialog) {
       DesktopMediaID dialog_window_id(DesktopMediaID::TYPE_WINDOW,
@@ -309,39 +285,30 @@
 
     {
       testing::InSequence dummy;
-      size_t source_count = screen ? window_count + 1 : window_count;
-      for (size_t i = 0; i < source_count; ++i) {
+      for (size_t i = 0; i < window_count; ++i) {
         EXPECT_CALL(observer_, OnSourceAdded(model_.get(), i))
             .WillOnce(CheckListSize(model_.get(), static_cast<int>(i + 1)));
       }
-      for (size_t i = 0; i < source_count - 1; ++i) {
+      for (size_t i = 0; i < window_count - 1; ++i) {
         EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), i));
       }
       EXPECT_CALL(observer_,
-                  OnSourceThumbnailChanged(model_.get(), source_count - 1))
+                  OnSourceThumbnailChanged(model_.get(), window_count - 1))
           .WillOnce(
               QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop));
     }
     model_->StartUpdating(&observer_);
     run_loop.Run();
 
-    if (screen) {
-      EXPECT_EQ(model_->GetSource(0).id.type, DesktopMediaID::TYPE_SCREEN);
-      EXPECT_EQ(model_->GetSource(0).id.id, 0);
-    }
-
     for (size_t i = 0; i < window_count; ++i) {
-      size_t source_index = screen ? i + 1 : i;
-      EXPECT_EQ(model_->GetSource(source_index).id.type,
-                DesktopMediaID::TYPE_WINDOW);
-      EXPECT_EQ(model_->GetSource(source_index).name,
-                base::UTF8ToUTF16("Test window"));
+      EXPECT_EQ(model_->GetSource(i).id.type, DesktopMediaID::TYPE_WINDOW);
+      EXPECT_EQ(model_->GetSource(i).name, base::UTF8ToUTF16("Test window"));
       int index = has_view_dialog ? i + 1 : i;
       int native_id = window_list_[index].id;
-      EXPECT_EQ(model_->GetSource(source_index).id.id, native_id);
+      EXPECT_EQ(model_->GetSource(i).id.id, native_id);
 #if defined(USE_AURA)
       if (i >= aura_window_first_index)
-        EXPECT_EQ(model_->GetSource(source_index).id.aura_id,
+        EXPECT_EQ(model_->GetSource(i).id.aura_id,
                   native_aura_id_map_[native_id]);
 #endif
     }
@@ -365,29 +332,48 @@
   DISALLOW_COPY_AND_ASSIGN(NativeDesktopMediaListTest);
 };
 
-TEST_F(NativeDesktopMediaListTest, WindowsOnly) {
-  AddWindowsAndVerify(false, kDefaultWindowCount, kDefaultAuraCount, false);
+TEST_F(NativeDesktopMediaListTest, Windows) {
+  AddWindowsAndVerify(false);
 }
 
 TEST_F(NativeDesktopMediaListTest, ScreenOnly) {
-  AddWindowsAndVerify(true, 0, 0, false);
+  model_ = base::MakeUnique<NativeDesktopMediaList>(
+      DesktopMediaID::TYPE_SCREEN, base::MakeUnique<FakeScreenCapturer>());
+
+  // Set update period to reduce the time it takes to run tests.
+  model_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(20));
+
+  base::RunLoop run_loop;
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(model_.get(), 0))
+        .WillOnce(CheckListSize(model_.get(), 1));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), 0))
+        .WillOnce(QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop));
+  }
+  model_->StartUpdating(&observer_);
+  run_loop.Run();
+
+  EXPECT_EQ(model_->GetSource(0).id.type, DesktopMediaID::TYPE_SCREEN);
+  EXPECT_EQ(model_->GetSource(0).id.id, 0);
 }
 
 // Verifies that the window specified with SetViewDialogWindowId() is filtered
 // from the results.
-TEST_F(NativeDesktopMediaListTest, Filtering) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, true);
+TEST_F(NativeDesktopMediaListTest, WindowFiltering) {
+  AddWindowsAndVerify(true);
 }
 
 TEST_F(NativeDesktopMediaListTest, AddNativeWindow) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  const int index = kDefaultWindowCount + 1;
+  const int index = kDefaultWindowCount;
   EXPECT_CALL(observer_, OnSourceAdded(model_.get(), index))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), index + 1),
+          DoAll(CheckListSize(model_.get(), kDefaultWindowCount + 1),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   AddNativeWindow(index);
@@ -401,14 +387,14 @@
 
 #if defined(ENABLE_AURA_WINDOW_TESTS)
 TEST_F(NativeDesktopMediaListTest, AddAuraWindow) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  const int index = kDefaultWindowCount + 1;
+  const int index = kDefaultWindowCount;
   EXPECT_CALL(observer_, OnSourceAdded(model_.get(), index))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), index + 1),
+          DoAll(CheckListSize(model_.get(), kDefaultWindowCount + 1),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   AddAuraWindow();
@@ -425,13 +411,13 @@
 #endif  // defined(ENABLE_AURA_WINDOW_TESTS)
 
 TEST_F(NativeDesktopMediaListTest, RemoveNativeWindow) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 1))
+  EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 0))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), kDefaultWindowCount),
+          DoAll(CheckListSize(model_.get(), kDefaultWindowCount - 1),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   window_list_.erase(window_list_.begin());
@@ -442,14 +428,14 @@
 
 #if defined(ENABLE_AURA_WINDOW_TESTS)
 TEST_F(NativeDesktopMediaListTest, RemoveAuraWindow) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  int aura_window_start_index = kDefaultWindowCount - kDefaultAuraCount + 1;
+  int aura_window_start_index = kDefaultWindowCount - kDefaultAuraCount;
   EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), aura_window_start_index))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), kDefaultWindowCount),
+          DoAll(CheckListSize(model_.get(), kDefaultWindowCount - 1),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   RemoveAuraWindow(0);
@@ -460,18 +446,18 @@
 #endif  // defined(ENABLE_AURA_WINDOW_TESTS)
 
 TEST_F(NativeDesktopMediaListTest, RemoveAllWindows) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
   testing::InSequence seq;
   for (int i = 0; i < kDefaultWindowCount - 1; i++) {
-    EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 1))
-        .WillOnce(CheckListSize(model_.get(), kDefaultWindowCount - i));
+    EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 0))
+        .WillOnce(CheckListSize(model_.get(), kDefaultWindowCount - i - 1));
   }
-  EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 1))
+  EXPECT_CALL(observer_, OnSourceRemoved(model_.get(), 0))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), 1),
+          DoAll(CheckListSize(model_.get(), 0),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   window_list_.clear();
@@ -481,11 +467,11 @@
 }
 
 TEST_F(NativeDesktopMediaListTest, UpdateTitle) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  EXPECT_CALL(observer_, OnSourceNameChanged(model_.get(), 1))
+  EXPECT_CALL(observer_, OnSourceNameChanged(model_.get(), 0))
       .WillOnce(QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop));
 
   const std::string kTestTitle = "New Title";
@@ -494,38 +480,38 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(model_->GetSource(1).name, base::UTF8ToUTF16(kTestTitle));
+  EXPECT_EQ(model_->GetSource(0).name, base::UTF8ToUTF16(kTestTitle));
 }
 
 TEST_F(NativeDesktopMediaListTest, UpdateThumbnail) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   // Aura windows' thumbnails may unpredictably change over time.
   for (size_t i = kDefaultWindowCount - kDefaultAuraCount;
        i < kDefaultWindowCount; ++i) {
-    EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), i + 1))
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), i))
         .Times(testing::AnyNumber());
   }
 
   base::RunLoop run_loop;
 
-  EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), 1))
+  EXPECT_CALL(observer_, OnSourceThumbnailChanged(model_.get(), 0))
       .WillOnce(QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop));
 
   // Update frame for the window and verify that we get notification about it.
-  window_capturer_->SetNextFrameValue(1, 10);
+  window_capturer_->SetNextFrameValue(0, 10);
 
   run_loop.Run();
 }
 
 TEST_F(NativeDesktopMediaListTest, MoveWindow) {
-  AddWindowsAndVerify(true, kDefaultWindowCount, kDefaultAuraCount, false);
+  AddWindowsAndVerify(false);
 
   base::RunLoop run_loop;
 
-  EXPECT_CALL(observer_, OnSourceMoved(model_.get(), 2, 1))
+  EXPECT_CALL(observer_, OnSourceMoved(model_.get(), 1, 0))
       .WillOnce(
-          DoAll(CheckListSize(model_.get(), kDefaultWindowCount + 1),
+          DoAll(CheckListSize(model_.get(), kDefaultWindowCount),
                 QuitRunLoop(base::ThreadTaskRunnerHandle::Get(), &run_loop)));
 
   std::swap(window_list_[0], window_list_[1]);
diff --git a/chrome/browser/net/nss_context.h b/chrome/browser/net/nss_context.h
index ca62c4a..3c6f2b5 100644
--- a/chrome/browser/net/nss_context.h
+++ b/chrome/browser/net/nss_context.h
@@ -22,20 +22,6 @@
 class ResourceContext;
 }  // namespace content
 
-// Returns a reference to the public slot for the user associated with
-// |context|.  Should be called only on the IO thread.
-crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
-    content::ResourceContext* context);
-
-// Returns a reference to the private slot for the user associated with
-// |context|, if it is loaded. If it is not loaded and |callback| is non-null,
-// the |callback| will be run once the slot is loaded.
-// Should be called only on the IO thread.
-crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
-    content::ResourceContext* context,
-    const base::Callback<void(crypto::ScopedPK11Slot)>& callback)
-    WARN_UNUSED_RESULT;
-
 // Returns a pointer to the NSSCertDatabase for the user associated with
 // |context|, if it is ready. If it is not ready and |callback| is non-null, the
 // |callback| will be run once the DB is initialized. Ownership is not
diff --git a/chrome/browser/net/nss_context_chromeos.cc b/chrome/browser/net/nss_context_chromeos.cc
index 7162668..888a3be 100644
--- a/chrome/browser/net/nss_context_chromeos.cc
+++ b/chrome/browser/net/nss_context_chromeos.cc
@@ -118,19 +118,6 @@
 
 }  // namespace
 
-crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
-    content::ResourceContext* context) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return crypto::GetPublicSlotForChromeOSUser(GetUsername(context));
-}
-
-crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
-    content::ResourceContext* context,
-    const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return crypto::GetPrivateSlotForChromeOSUser(GetUsername(context), callback);
-}
-
 net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
     content::ResourceContext* context,
     const base::Callback<void(net::NSSCertDatabase*)>& callback) {
diff --git a/chrome/browser/net/nss_context_linux.cc b/chrome/browser/net/nss_context_linux.cc
index 8e61715..c649bbd 100644
--- a/chrome/browser/net/nss_context_linux.cc
+++ b/chrome/browser/net/nss_context_linux.cc
@@ -12,19 +12,6 @@
 net::NSSCertDatabase* g_nss_cert_database = NULL;
 }  // namespace
 
-crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
-    content::ResourceContext* context) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return crypto::ScopedPK11Slot(crypto::GetPersistentNSSKeySlot());
-}
-
-crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
-    content::ResourceContext* context,
-    const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return crypto::ScopedPK11Slot(crypto::GetPersistentNSSKeySlot());
-}
-
 net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
     content::ResourceContext* context,
     const base::Callback<void(net::NSSCertDatabase*)>& callback) {
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index e4201a7..ccb3d65d 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -20,95 +20,82 @@
 
     /**
      * Used to fetch local print destinations.
-     * @type {!print_preview.NativeLayer}
-     * @private
+     * @private {!print_preview.NativeLayer}
      */
     this.nativeLayer_ = nativeLayer;
 
     /**
      * User information repository.
-     * @type {!print_preview.UserInfo}
-     * @private
+     * @private {!print_preview.UserInfo}
      */
     this.userInfo_ = userInfo;
 
     /**
      * Used to load and persist the selected destination.
-     * @type {!print_preview.AppState}
-     * @private
+     * @private {!print_preview.AppState}
      */
     this.appState_ = appState;
 
     /**
      * Used to track metrics.
-     * @type {!print_preview.DestinationSearchMetricsContext}
-     * @private
+     * @private {!print_preview.DestinationSearchMetricsContext}
      */
     this.metrics_ = new print_preview.DestinationSearchMetricsContext();
 
     /**
      * Internal backing store for the data store.
-     * @type {!Array<!print_preview.Destination>}
-     * @private
+     * @private {!Array<!print_preview.Destination>}
      */
     this.destinations_ = [];
 
     /**
      * Cache used for constant lookup of destinations by origin and id.
-     * @type {Object<!print_preview.Destination>}
-     * @private
+     * @private {Object<!print_preview.Destination>}
      */
     this.destinationMap_ = {};
 
     /**
      * Currently selected destination.
-     * @type {print_preview.Destination}
-     * @private
+     * @private {print_preview.Destination}
      */
     this.selectedDestination_ = null;
 
     /**
      * Whether the destination store will auto select the destination that
      * matches this set of parameters.
-     * @type {print_preview.DestinationMatch}
-     * @private
+     * @private {print_preview.DestinationMatch}
      */
     this.autoSelectMatchingDestination_ = null;
 
     /**
      * Event tracker used to track event listeners of the destination store.
-     * @type {!EventTracker}
-     * @private
+     * @private {!EventTracker}
      */
     this.tracker_ = new EventTracker();
 
     /**
      * Whether PDF printer is enabled. It's disabled, for example, in App Kiosk
      * mode.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.pdfPrinterEnabled_ = false;
 
     /**
      * ID of the system default destination.
-     * @type {?string}
-     * @private
+     * @private {?string}
      */
     this.systemDefaultDestinationId_ = null;
 
     /**
      * Used to fetch cloud-based print destinations.
-     * @type {cloudprint.CloudPrintInterface}
-     * @private
+     * @private {cloudprint.CloudPrintInterface}
      */
     this.cloudPrintInterface_ = null;
 
     /**
      * Maps user account to the list of origins for which destinations are
      * already loaded.
-     * @type {!Object<Array<print_preview.DestinationOrigin>>}
-     * @private
+     * @private {!Object<Array<!print_preview.DestinationOrigin>>}
      */
     this.loadedCloudOrigins_ = {};
 
@@ -117,84 +104,66 @@
      * destination matches the initial destination ID after the specified
      * timeout, the first destination in the store will be automatically
      * selected.
-     * @type {?number}
-     * @private
+     * @private {?number}
      */
     this.autoSelectTimeout_ = null;
 
     /**
      * Whether a search for local destinations is in progress.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.isLocalDestinationSearchInProgress_ = false;
 
     /**
      * Whether the destination store has already loaded or is loading all local
      * destinations.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.hasLoadedAllLocalDestinations_ = false;
 
     /**
      * Whether a search for privet destinations is in progress.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.isPrivetDestinationSearchInProgress_ = false;
 
     /**
      * Whether the destination store has already loaded or is loading all privet
      * destinations.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.hasLoadedAllPrivetDestinations_ = false;
 
     /**
-     * ID of a timeout after the start of a privet search to end that privet
-     * search.
-     * @type {?number}
-     * @private
-     */
-    this.privetSearchTimeout_ = null;
-
-    /**
      * Whether a search for extension destinations is in progress.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.isExtensionDestinationSearchInProgress_ = false;
 
     /**
      * Whether the destination store has already loaded all extension
      * destinations.
-     * @type {boolean}
-     * @private
+     * @private {boolean}
      */
     this.hasLoadedAllExtensionDestinations_ = false;
 
     /**
      * ID of a timeout set at the start of an extension destination search. The
      * timeout ends the search.
-     * @type {?number}
-     * @private
+     * @private {?number}
      */
     this.extensionSearchTimeout_ = null;
 
     /**
      * MDNS service name of destination that we are waiting to register.
-     * @type {?string}
-     * @private
+     * @private {?string}
      */
     this.waitForRegisterDestination_ = null;
 
     /**
      * Local destinations are CROS destinations on ChromeOS because they require
      * extra setup.
-     * @type {!print_preview.DestinationOrigin}
-     * @private
+     * @private {!print_preview.DestinationOrigin}
      */
     this.platformOrigin_ = cr.isChromeOS ?
         print_preview.DestinationOrigin.CROS :
@@ -235,13 +204,6 @@
   DestinationStore.AUTO_SELECT_TIMEOUT_ = 15000;
 
   /**
-   * Amount of time spent searching for privet destination, in milliseconds.
-   * @private {number}
-   * @const
-   */
-  DestinationStore.PRIVET_SEARCH_DURATION_ = 5000;
-
-  /**
    * Maximum amount of time spent searching for extension destinations, in
    * milliseconds.
    * @private {number}
@@ -595,6 +557,8 @@
       this.pdfPrinterEnabled_ = !isInAppKioskMode;
       this.systemDefaultDestinationId_ = systemDefaultDestinationId;
       this.createLocalPdfPrintDestination_();
+      cr.addWebUIListener('privet-printer-added',
+                         this.onPrivetPrinterAdded_.bind(this));
       cr.addWebUIListener('extension-printers-added',
           this.onExtensionPrintersAdded_.bind(this));
 
@@ -725,7 +689,8 @@
       if (origin == print_preview.DestinationOrigin.PRIVET) {
         // TODO(noamsml): Resolve a specific printer instead of listing all
         // privet printers in this case.
-        this.nativeLayer_.startGetPrivetDestinations();
+        this.nativeLayer_.getPrivetPrinters().then(
+            this.endPrivetPrinterSearch_.bind(this));
 
         // Create a fake selectedDestination_ that is not actually in the
         // destination store. When the real destination is created, this
@@ -1090,17 +1055,18 @@
 
     /** Initiates loading of privet print destinations. */
     startLoadPrivetDestinations: function() {
-      if (!this.hasLoadedAllPrivetDestinations_) {
-        if (this.privetDestinationSearchInProgress_)
-          clearTimeout(this.privetSearchTimeout_);
-        this.isPrivetDestinationSearchInProgress_ = true;
-        this.nativeLayer_.startGetPrivetDestinations();
-        cr.dispatchSimpleEvent(
-            this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
-        this.privetSearchTimeout_ = setTimeout(
-            this.endPrivetPrinterSearch_.bind(this),
-            DestinationStore.PRIVET_SEARCH_DURATION_);
-      }
+      if (this.hasLoadedAllPrivetDestinations_)
+        return;
+      this.isPrivetDestinationSearchInProgress_ = true;
+      this.nativeLayer_.getPrivetPrinters().then(
+          this.endPrivetPrinterSearch_.bind(this),
+          function() {
+            // Rejected by C++, indicating privet printing is disabled.
+            this.hasLoadedAllPrivetDestinations_ = true;
+            this.isPrivetDestinationSearchInProgress_ = false;
+          }.bind(this));
+      cr.dispatchSimpleEvent(
+         this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
     },
 
     /** Initializes loading of extension managed print destinations. */
@@ -1163,7 +1129,8 @@
      * Wait for a privet device to be registered.
      */
     waitForRegister: function(id) {
-      this.nativeLayer_.startGetPrivetDestinations();
+      this.nativeLayer_.getPrivetPrinters().then(
+          this.endPrivetPrinterSearch_.bind(this));
       this.waitForRegisterDestination_ = id;
     },
 
@@ -1302,7 +1269,6 @@
      * @private
      */
     endPrivetPrinterSearch_: function() {
-      this.nativeLayer_.stopGetPrivetDestinations();
       this.isPrivetDestinationSearchInProgress_ = false;
       this.hasLoadedAllPrivetDestinations_ = true;
       cr.dispatchSimpleEvent(
@@ -1378,10 +1344,6 @@
           this.onDestinationsReload_.bind(this));
       this.tracker_.add(
           nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.PRIVET_PRINTER_CHANGED,
-          this.onPrivetPrinterAdded_.bind(this));
-      this.tracker_.add(
-          nativeLayerEventTarget,
           print_preview.NativeLayer.EventType.PRIVET_CAPABILITIES_SET,
           this.onPrivetCapabilitiesSet_.bind(this));
       this.tracker_.add(
@@ -1588,22 +1550,21 @@
 
     /**
      * Called when a Privet printer is added to the local network.
-     * @param {{printer: {serviceName: string,
-     *                    name: string,
-     *                    hasLocalPrinting: boolean,
-     *                    isUnregistered: boolean,
-     *                    cloudID: string}}} event Contains information about
-     *      the added printer.
+     * @param {!{serviceName: string,
+     *           name: string,
+     *           hasLocalPrinting: boolean,
+     *           isUnregistered: boolean,
+     *           cloudID: string}} printer Information about the added printer.
      * @private
      */
-    onPrivetPrinterAdded_: function(event) {
-      if (event.printer.serviceName == this.waitForRegisterDestination_ &&
-          !event.printer.isUnregistered) {
+    onPrivetPrinterAdded_: function(printer) {
+      if (printer.serviceName == this.waitForRegisterDestination_ &&
+          !printer.isUnregistered) {
         this.waitForRegisterDestination_ = null;
         this.onDestinationsReload_();
       } else {
         this.insertDestinations_(
-            print_preview.PrivetDestinationParser.parse(event.printer));
+            print_preview.PrivetDestinationParser.parse(printer));
       }
     },
 
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 6897affa..f259e5e 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -69,7 +69,6 @@
     global.onDidPreviewPage = this.onDidPreviewPage_.bind(this);
     global.updatePrintPreview = this.onUpdatePrintPreview_.bind(this);
     global.onDidGetAccessToken = this.onDidGetAccessToken_.bind(this);
-    global.onPrivetPrinterChanged = this.onPrivetPrinterChanged_.bind(this);
     global.onPrivetCapabilitiesSet =
         this.onPrivetCapabilitiesSet_.bind(this);
     global.onPrivetPrintFailed = this.onPrivetPrintFailed_.bind(this);
@@ -232,20 +231,13 @@
     },
 
     /**
-     * Requests the network's privet print destinations. A number of
-     * PRIVET_PRINTER_CHANGED events will be fired in response, followed by a
-     * PRIVET_SEARCH_ENDED.
+     * Requests the network's privet print destinations. After this is called,
+     * a number of privet-printer-changed events may be fired.
+     * @return {!Promise} Resolves when privet printer search is completed.
+     *     Rejected if privet printers are not enabled.
      */
-    startGetPrivetDestinations: function() {
-      chrome.send('getPrivetPrinters');
-    },
-
-    /**
-     * Requests that the privet print stack stop searching for privet print
-     * destinations.
-     */
-    stopGetPrivetDestinations: function() {
-      chrome.send('stopGetPrivetPrinters');
+    getPrivetPrinters: function() {
+      return cr.sendWithPromise('getPrivetPrinters');
     },
 
     /**
@@ -782,18 +774,6 @@
     },
 
     /**
-     * @param {{serviceName: string, name: string}} printer Specifies
-     *     information about the printer that was added.
-     * @private
-     */
-    onPrivetPrinterChanged_: function(printer) {
-      var privetPrinterChangedEvent =
-            new Event(NativeLayer.EventType.PRIVET_PRINTER_CHANGED);
-      privetPrinterChangedEvent.printer = printer;
-      this.eventTarget_.dispatchEvent(privetPrinterChangedEvent);
-    },
-
-    /**
      * @param {Object} printer Specifies information about the printer that was
      *    added.
      * @private
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 9dff41e..11a6a60 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -486,6 +486,7 @@
     "//chrome/common:instant_mojom",
     "//chrome/common/net",
     "//chrome/installer/util:with_no_strings",
+    "//components/about_ui",
     "//components/app_modal",
     "//components/autofill/content/browser:risk_proto",
     "//components/autofill/core/browser",
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 6445c77..2ab0399 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -37,10 +37,6 @@
 class Extension;
 }
 
-namespace gfx {
-class Point;
-}
-
 namespace net {
 class AuthChallengeInfo;
 class URLRequest;
@@ -124,23 +120,6 @@
 
 #if defined(OS_MACOSX)
 
-// Shows a views zoom bubble at the |anchor_point|. This occurs when the zoom
-// icon is clicked or when a shortcut key is pressed or whenever |web_contents|
-// zoom factor changes. |user_action| is used to determine if the bubble will
-// auto-close.
-void ShowZoomBubbleViewsAtPoint(content::WebContents* web_contents,
-                                const gfx::Point& anchor_point,
-                                bool user_action);
-
-// Closes a views zoom bubble if currently shown.
-void CloseZoomBubbleViews();
-
-// Refreshes views zoom bubble if currently shown.
-void RefreshZoomBubbleViews();
-
-// Returns true if views zoom bubble is currently shown.
-bool IsZoomBubbleViewsShown();
-
 // Bridging methods that show/hide the toolkit-views based Task Manager on Mac.
 task_manager::TaskManagerTableModel* ShowTaskManagerViews(Browser* browser);
 void HideTaskManagerViews();
diff --git a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.h b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.h
index 7293b4b..474e0fe 100644
--- a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.h
+++ b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.h
@@ -7,7 +7,7 @@
 
 #include "base/mac/scoped_block.h"
 #include "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/base_bubble_controller.h"
+#import "chrome/browser/ui/cocoa/omnibox_decoration_bubble_controller.h"
 #import "ui/base/cocoa/tracking_area.h"
 
 namespace content {
@@ -30,7 +30,7 @@
 // The ZoomBubbleController is used to display the current page zoom percent
 // when not at the user's default. It is opened by the ZoomDecoration in the
 // location bar.
-@interface ZoomBubbleController : BaseBubbleController {
+@interface ZoomBubbleController : OmniboxDecorationBubbleController {
  @private
   ZoomBubbleControllerDelegate* delegate_;
 
diff --git a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
index 0f9c199..24624dd3 100644
--- a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
@@ -8,8 +8,11 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
+#import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
+#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#import "chrome/browser/ui/cocoa/location_bar/zoom_decoration.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/zoom/page_zoom.h"
 #include "components/zoom/zoom_controller.h"
@@ -175,6 +178,15 @@
   [self close];
 }
 
+// OmniboxDecorationBubbleController implementation.
+- (LocationBarDecoration*)decorationForBubble {
+  BrowserWindowController* controller = [BrowserWindowController
+      browserWindowControllerForWindow:[self parentWindow]];
+  LocationBarViewMac* locationBar = [controller locationBarBridge];
+  return locationBar ? locationBar->zoom_decoration() : nullptr;
+}
+
+// NSWindowController implementation.
 - (void)windowWillClose:(NSNotification*)notification {
   // |delegate_| may be set null by this object's owner.
   if (delegate_) {
diff --git a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
index f04072b..1494ed4 100644
--- a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
+++ b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
@@ -72,13 +72,14 @@
 
 void ShowZoomBubbleViewsAtPoint(content::WebContents* web_contents,
                                 const gfx::Point& anchor_point,
-                                bool user_action) {
+                                bool user_action,
+                                LocationBarDecoration* decoration) {
   ZoomBubbleView::ShowBubble(web_contents, anchor_point,
                              user_action
                                  ? LocationBarBubbleDelegateView::USER_GESTURE
                                  : LocationBarBubbleDelegateView::AUTOMATIC);
   if (ZoomBubbleView::GetZoomBubble())
-    KeepBubbleAnchored(ZoomBubbleView::GetZoomBubble());
+    KeepBubbleAnchored(ZoomBubbleView::GetZoomBubble(), decoration);
 }
 
 void CloseZoomBubbleViews() {
diff --git a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.h b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.h
index bdc1fb5..15f5cfe 100644
--- a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.h
+++ b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.h
@@ -51,6 +51,24 @@
                                     bool newly_bookmarked,
                                     LocationBarDecoration* decoration);
 
+// Shows a views zoom bubble at the |anchor_point|. This occurs when the zoom
+// icon is clicked or when a shortcut key is pressed or whenever |web_contents|
+// zoom factor changes. |user_action| is used to determine if the bubble will
+// auto-close.
+void ShowZoomBubbleViewsAtPoint(content::WebContents* web_contents,
+                                const gfx::Point& anchor_point,
+                                bool user_action,
+                                LocationBarDecoration* decoration);
+
+// Closes a views zoom bubble if currently shown.
+void CloseZoomBubbleViews();
+
+// Refreshes views zoom bubble if currently shown.
+void RefreshZoomBubbleViews();
+
+// Returns true if views zoom bubble is currently shown.
+bool IsZoomBubbleViewsShown();
+
 // This is a class so that it can be friended from ContentSettingBubbleContents,
 // which allows it to call SetAnchorRect().
 class ContentSettingBubbleViewsBridge {
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
index 77599e10..fdcbcec 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
@@ -193,6 +193,8 @@
     return translate_decoration_.get();
   }
 
+  ZoomDecoration* zoom_decoration() const { return zoom_decoration_.get(); }
+
   Browser* browser() const { return browser_; }
 
   // ZoomManagerObserver:
diff --git a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
index 807dc21..9d615ab 100644
--- a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
@@ -9,7 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/cocoa/browser_dialogs_views_mac.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/l10n_util.h"
 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
@@ -95,8 +95,8 @@
     NSPoint anchor = [browser_window_controller bookmarkBubblePoint];
     gfx::Point anchor_point = gfx::ScreenPointFromNSPoint(
         ui::ConvertPointFromWindowToScreen(window, anchor));
-    chrome::ShowZoomBubbleViewsAtPoint(web_contents, anchor_point,
-                                       auto_close == NO /* user_action */);
+    chrome::ShowZoomBubbleViewsAtPoint(
+        web_contents, anchor_point, auto_close == NO /* user_action */, this);
     return;
   }
 
diff --git a/chrome/browser/ui/crypto_module_delegate_nss.cc b/chrome/browser/ui/crypto_module_delegate_nss.cc
index 7211e8a..a296380 100644
--- a/chrome/browser/ui/crypto_module_delegate_nss.cc
+++ b/chrome/browser/ui/crypto_module_delegate_nss.cc
@@ -7,66 +7,21 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "chrome/browser/net/nss_context.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
 
-namespace {
-
-void CreateWithSlot(
-    chrome::CryptoModulePasswordReason reason,
-    const net::HostPortPair& server,
-    const base::Callback<void(std::unique_ptr<ChromeNSSCryptoModuleDelegate>)>&
-        callback,
-    crypto::ScopedPK11Slot slot) {
-  if (!slot) {
-    callback.Run(std::unique_ptr<ChromeNSSCryptoModuleDelegate>());
-    return;
-  }
-  callback.Run(std::unique_ptr<ChromeNSSCryptoModuleDelegate>(
-      new ChromeNSSCryptoModuleDelegate(reason, server, std::move(slot))));
-}
-
-}  // namespace
-
 ChromeNSSCryptoModuleDelegate::ChromeNSSCryptoModuleDelegate(
     chrome::CryptoModulePasswordReason reason,
-    const net::HostPortPair& server,
-    crypto::ScopedPK11Slot slot)
+    const net::HostPortPair& server)
     : reason_(reason),
       server_(server),
       event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
              base::WaitableEvent::InitialState::NOT_SIGNALED),
-      cancelled_(false),
-      slot_(std::move(slot)) {}
+      cancelled_(false) {}
 
 ChromeNSSCryptoModuleDelegate::~ChromeNSSCryptoModuleDelegate() {}
 
-// static
-void ChromeNSSCryptoModuleDelegate::CreateForResourceContext(
-    chrome::CryptoModulePasswordReason reason,
-    const net::HostPortPair& server,
-    content::ResourceContext* context,
-    const base::Callback<void(std::unique_ptr<ChromeNSSCryptoModuleDelegate>)>&
-        callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!callback.is_null());
-
-  base::Callback<void(crypto::ScopedPK11Slot)> get_slot_callback =
-      base::Bind(&CreateWithSlot, reason, server, callback);
-
-  crypto::ScopedPK11Slot slot =
-      GetPrivateNSSKeySlotForResourceContext(context, get_slot_callback);
-  if (slot)
-    get_slot_callback.Run(std::move(slot));
-}
-
-// TODO(mattm): allow choosing which slot to generate and store the key.
-crypto::ScopedPK11Slot ChromeNSSCryptoModuleDelegate::RequestSlot() {
-  return std::move(slot_);
-}
-
 std::string ChromeNSSCryptoModuleDelegate::RequestPassword(
     const std::string& slot_name,
     bool retry,
@@ -114,9 +69,5 @@
 CreateCryptoModuleBlockingPasswordDelegate(
     chrome::CryptoModulePasswordReason reason,
     const net::HostPortPair& server) {
-  // Returns a ChromeNSSCryptoModuleDelegate without Pk11Slot. Since it is only
-  // being used as a CryptoModuleBlockingDialogDelegate, using a slot handle is
-  // unnecessary.
-  return new ChromeNSSCryptoModuleDelegate(
-      reason, server, crypto::ScopedPK11Slot());
+  return new ChromeNSSCryptoModuleDelegate(reason, server);
 }
diff --git a/chrome/browser/ui/crypto_module_delegate_nss.h b/chrome/browser/ui/crypto_module_delegate_nss.h
index 45029f8..3af79b9 100644
--- a/chrome/browser/ui/crypto_module_delegate_nss.h
+++ b/chrome/browser/ui/crypto_module_delegate_nss.h
@@ -7,45 +7,26 @@
 
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/synchronization/waitable_event.h"
 #include "chrome/browser/ui/crypto_module_password_dialog.h"
 #include "crypto/nss_crypto_module_delegate.h"
 #include "net/base/host_port_pair.h"
 
-namespace content {
-class ResourceContext;
-}
-
 // Delegate to handle unlocking a slot or indicating which slot to store a key
 // in. When passing to NSS functions which take a wincx argument, use the value
 // returned from the wincx() method.
 class ChromeNSSCryptoModuleDelegate
-    : public crypto::NSSCryptoModuleDelegate {
+    : public crypto::CryptoModuleBlockingPasswordDelegate {
  public:
   // Create a ChromeNSSCryptoModuleDelegate. |reason| is used to select what
   // string to show the user, |server| is displayed to indicate which connection
   // is causing the dialog to appear. |slot| can be NULL.
   ChromeNSSCryptoModuleDelegate(chrome::CryptoModulePasswordReason reason,
-                                const net::HostPortPair& server,
-                                crypto::ScopedPK11Slot slot);
+                                const net::HostPortPair& server);
 
   ~ChromeNSSCryptoModuleDelegate() override;
 
-  // Must be called on IO thread. Creates a delegate and returns it
-  // synchronously or asynchronously to |callback|. If the delegate could not be
-  // created, |callback| is called with NULL.
-  static void CreateForResourceContext(
-      chrome::CryptoModulePasswordReason reason,
-      const net::HostPortPair& server,
-      content::ResourceContext* context,
-      const base::Callback<
-          void(std::unique_ptr<ChromeNSSCryptoModuleDelegate>)>& callback);
-
-  // crypto::NSSCryptoModuleDelegate implementation.
-  crypto::ScopedPK11Slot RequestSlot() override;
-
   // crypto::CryptoModuleBlockingPasswordDelegate implementation.
   std::string RequestPassword(const std::string& slot_name,
                               bool retry,
@@ -67,9 +48,6 @@
   std::string password_;
   bool cancelled_;
 
-  // The slot which will be returned by RequestSlot.
-  crypto::ScopedPK11Slot slot_;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeNSSCryptoModuleDelegate);
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 38aeef5..babe43fc 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -118,8 +118,8 @@
   const SkColor colors[2] = { color, SkColorSetA(color, 0) };
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
-  flags.setShader(cc::WrapSkShader(SkGradientShader::MakeRadial(
-      p, radius, colors, nullptr, 2, SkShader::kClamp_TileMode)));
+  flags.setShader(cc::PaintShader::MakeRadialGradient(
+      p, radius, colors, nullptr, 2, SkShader::kClamp_TileMode));
   canvas->sk_canvas()->drawRect(
       SkRect::MakeXYWH(p.x() - radius, p.y() - radius, radius * 2, radius * 2),
       flags);
diff --git a/chrome/browser/ui/webui/DEPS b/chrome/browser/ui/webui/DEPS
index e54b94ad..f866a1e 100644
--- a/chrome/browser/ui/webui/DEPS
+++ b/chrome/browser/ui/webui/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/about_ui"
   "+components/invalidation",
   "+components/onc",
   "+components/proximity_auth",
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index 67182676..1216a073 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -49,6 +49,7 @@
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/about_ui/credit_utils.h"
 #include "components/grit/components_resources.h"
 #include "components/strings/grit/components_locale_settings.h"
 #include "content/public/browser/browser_thread.h"
@@ -691,32 +692,14 @@
       idr = IDR_KEYBOARD_UTILS_JS;
 #endif
 
-    base::StringPiece raw_response =
-        ResourceBundle::GetSharedInstance().GetRawDataResource(idr);
     if (idr == IDR_ABOUT_UI_CREDITS_HTML) {
-      const uint8_t* next_encoded_byte =
-          reinterpret_cast<const uint8_t*>(raw_response.data());
-      size_t input_size_remaining = raw_response.size();
-      BrotliDecoderState* decoder =
-          BrotliDecoderCreateInstance(nullptr /* no custom allocator */,
-                                      nullptr /* no custom deallocator */,
-                                      nullptr /* no custom memory handle */);
-      CHECK(!!decoder);
-      while (!BrotliDecoderIsFinished(decoder)) {
-        size_t output_size_remaining = 0;
-        CHECK(BrotliDecoderDecompressStream(
-                  decoder, &input_size_remaining, &next_encoded_byte,
-                  &output_size_remaining, nullptr,
-                  nullptr) != BROTLI_DECODER_RESULT_ERROR);
-        const uint8_t* output_buffer =
-            BrotliDecoderTakeOutput(decoder, &output_size_remaining);
-        response.insert(response.end(), output_buffer,
-                        output_buffer + output_size_remaining);
-      }
-      BrotliDecoderDestroyInstance(decoder);
+      response = about_ui::GetCredits(true /*include_scripts*/);
     } else {
-      response = raw_response.as_string();
+      response = ResourceBundle::GetSharedInstance()
+                     .GetRawDataResource(idr)
+                     .as_string();
     }
+
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
   } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
     response = AboutDiscards(path);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 9edc6249..e0a11e502 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -199,6 +199,9 @@
 // Id of the predefined PDF printer.
 const char kLocalPdfPrinterId[] = "Save as PDF";
 
+// Timeout for searching for privet printers, in seconds.
+const int kPrivetTimeoutSec = 5;
+
 // Get the print job settings dictionary from |args|. The caller takes
 // ownership of the returned DictionaryValue. Returns NULL on failure.
 std::unique_ptr<base::DictionaryValue> GetSettingsDictionary(
@@ -615,9 +618,6 @@
   web_ui()->RegisterMessageCallback("getPrivetPrinters",
       base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinters,
                  base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("stopGetPrivetPrinters",
-      base::Bind(&PrintPreviewHandler::HandleStopGetPrivetPrinters,
-                 base::Unretained(this)));
   web_ui()->RegisterMessageCallback("getPrivetPrinterCapabilities",
       base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinterCapabilities,
                  base::Unretained(this)));
@@ -675,22 +675,32 @@
 }
 
 void PrintPreviewHandler::HandleGetPrivetPrinters(const base::ListValue* args) {
-  if (!PrivetPrintingEnabled())
-    return web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterSearchDone");
+  std::string callback_id;
+  CHECK(args->GetString(0, &callback_id));
+  CHECK(!callback_id.empty());
+
+  AllowJavascript();
+
+  if (!PrivetPrintingEnabled()) {
+    RejectJavascriptCallback(base::Value(callback_id), base::Value(false));
+  }
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   using local_discovery::ServiceDiscoverySharedClient;
   scoped_refptr<ServiceDiscoverySharedClient> service_discovery =
       ServiceDiscoverySharedClient::GetInstance();
+  DCHECK(privet_callback_id_.empty());
+  privet_callback_id_ = callback_id;
   StartPrivetLister(service_discovery);
 #endif
 }
 
-void PrintPreviewHandler::HandleStopGetPrivetPrinters(
-    const base::ListValue* args) {
+void PrintPreviewHandler::StopPrivetLister() {
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+  privet_lister_timer_.reset();
   if (PrivetPrintingEnabled() && printer_lister_) {
     printer_lister_->Stop();
   }
+  ResolveJavascriptCallback(base::Value(privet_callback_id_), base::Value());
 #endif
 }
 
@@ -1527,15 +1537,16 @@
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
 void PrintPreviewHandler::StartPrivetLister(const scoped_refptr<
     local_discovery::ServiceDiscoverySharedClient>& client) {
-  if (!PrivetPrintingEnabled())
-    return web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterSearchDone");
-
   Profile* profile = Profile::FromWebUI(web_ui());
   DCHECK(!service_discovery_client_.get() ||
          service_discovery_client_.get() == client.get());
   service_discovery_client_ = client;
   printer_lister_ = base::MakeUnique<cloud_print::PrivetLocalPrinterLister>(
       service_discovery_client_.get(), profile->GetRequestContext(), this);
+  privet_lister_timer_.reset(new base::OneShotTimer());
+  privet_lister_timer_->Start(FROM_HERE,
+                              base::TimeDelta::FromSeconds(kPrivetTimeoutSec),
+                              this, &PrintPreviewHandler::StopPrivetLister);
   printer_lister_->Start();
 }
 
@@ -1548,7 +1559,7 @@
       command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)) {
     base::DictionaryValue info;
     FillPrinterDescription(name, description, has_local_printing, &info);
-    web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterChanged", info);
+    FireWebUIListener("privet-printer-added", info);
   }
 }
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index a6c69fa..61133eb6 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "chrome/common/features.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "content/public/browser/web_ui_message_handler.h"
@@ -142,9 +143,6 @@
   // |args| is the provisional printer ID.
   void HandleGrantExtensionPrinterAccess(const base::ListValue* args);
 
-  // Stops getting all local privet printers. |arg| is unused.
-  void HandleStopGetPrivetPrinters(const base::ListValue* args);
-
   // Asks the initiator renderer to generate a preview.  First element of |args|
   // is a job settings JSON string.
   void HandleGetPreview(const base::ListValue* args);
@@ -286,6 +284,7 @@
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   void StartPrivetLister(const scoped_refptr<
       local_discovery::ServiceDiscoverySharedClient>& client);
+  void StopPrivetLister();
   void OnPrivetCapabilities(const base::DictionaryValue* capabilities);
   void PrivetCapabilitiesUpdateClient(
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
@@ -384,7 +383,7 @@
   scoped_refptr<local_discovery::ServiceDiscoverySharedClient>
       service_discovery_client_;
   std::unique_ptr<cloud_print::PrivetLocalPrinterLister> printer_lister_;
-
+  std::unique_ptr<base::OneShotTimer> privet_lister_timer_;
   std::unique_ptr<cloud_print::PrivetHTTPAsynchronousFactory>
       privet_http_factory_;
   std::unique_ptr<cloud_print::PrivetHTTPResolution> privet_http_resolution_;
@@ -403,6 +402,9 @@
   // notify the test if it was a successful save, only that it was attempted.
   base::Closure pdf_file_saved_closure_;
 
+  // Callback ID to be used to notify UI that privet search is finished.
+  std::string privet_callback_id_ = "";
+
   // Proxy for calls to the print backend.  Lazily initialized since web_ui() is
   // not available at construction time.
   std::unique_ptr<printing::PrinterBackendProxy> printer_backend_proxy_;
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index e9578be..17791d72 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -218,10 +218,6 @@
     {"engine_params", crash_keys::kMediumSize},
     {"engine1_params", crash_keys::kMediumSize},
     {"engine2_params", crash_keys::kMediumSize},
-
-    // Temporary for http://crbug.com/703649.
-    {"field_trial_shmem_create_error", crash_keys::kSmallSize},
-    {"field_trial_shmem_map_error", crash_keys::kSmallSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/background.js b/chrome/test/data/extensions/api_test/webrequest/policy_blocked/background.js
deleted file mode 100644
index 546ba78..0000000
--- a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/background.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Return messages to tell which URL requests are visible to the extension.
-chrome.webRequest.onBeforeRequest.addListener(function(details) {
-   if (details.url.indexOf('example2') != -1) {
-     chrome.test.sendMessage('protected_origin');
-   }
-   if (details.url.indexOf('protected_url') != -1) {
-     chrome.test.sendMessage('protected_url');
-   }
-}, {urls: ['<all_urls>']}, []);
diff --git a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/manifest.json b/chrome/test/data/extensions/api_test/webrequest/policy_blocked/manifest.json
deleted file mode 100644
index c414d5e..0000000
--- a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/manifest.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "name": "Web Request Policy Tests",
-  "version": "1",
-  "manifest_version": 2,
-  "permissions": ["webRequest", "<all_urls>"],
-  "background" : {
-    "scripts": ["background.js"]
-  }
-}
diff --git a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.html b/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.html
deleted file mode 100644
index 8271dc01..0000000
--- a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.html
+++ /dev/null
@@ -1,9 +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.
--->
-<html>
-  <body></body>
-  <script src="ref_remote_js.js"></script>
-</html>
diff --git a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.js b/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.js
deleted file mode 100644
index 600d094..0000000
--- a/chrome/test/data/extensions/api_test/webrequest/policy_blocked/ref_remote_js.js
+++ /dev/null
@@ -1,11 +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.
-
-// Injects a script tag with a source based on the current URL. The script
-// however will be served from a different domain (example2.com) and target a
-// different file which is blank. This lets us make a request with an initiator
-// of example.com and a URL of example2.com.
-var script = document.createElement('script');
-script.src = 'http://example2.com:' + location.port + '/empty.html';
-document.body.appendChild(script);
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 4e35e6b..51f80161 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -13,6 +13,7 @@
         'getInitialSettings',
         'getPrinters',
         'getExtensionPrinters',
+        'getPrivetPrinters',
         'setupPrinter'
       ]);
 
@@ -90,6 +91,12 @@
     },
 
     /** @override */
+    getPrivetPrinters: function() {
+      this.methodCalled('getPrivetPrinters');
+      return Promise.resolve(true);
+    },
+
+    /** @override */
     setupPrinter: function(printerId) {
       this.methodCalled('setupPrinter', printerId);
       return this.shouldRejectPrinterSetup_ ?
@@ -99,8 +106,6 @@
 
     /** Stubs for |print_preview.NativeLayer| methods that call C++ handlers. */
     previewReadyForTest: function() {},
-    startGetLocalDestinations: function() {},
-    startGetPrivetDestinations: function() {},
     startGetLocalDestinationCapabilities: function(destinationId) {
       if (destinationId == this.destinationToWatch_)
         this.getLocalDestinationCapabilitiesCallCount_++;
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 65c4b31..4f308fc7 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -34,7 +34,7 @@
 #include "chrome/common/resource_usage_reporter.mojom.h"
 #include "chrome/utility/media_router/dial_device_description_parser_impl.h"
 #include "chrome/utility/profile_import_handler.h"
-#include "net/proxy/mojo_proxy_resolver_factory_impl.h"
+#include "net/proxy/mojo_proxy_resolver_factory_impl.h"  // nogncheck
 #include "net/proxy/proxy_resolver_v8.h"
 #endif  // !defined(OS_ANDROID)
 
diff --git a/components/about_ui/BUILD.gn b/components/about_ui/BUILD.gn
new file mode 100644
index 0000000..b1b5949
--- /dev/null
+++ b/components/about_ui/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("about_ui") {
+  sources = [
+    "credit_utils.cc",
+    "credit_utils.h",
+  ]
+  deps = [
+    "//base",
+    "//components/resources:components_resources",
+    "//third_party/brotli:dec",
+    "//ui/base",
+    "//ui/resources",
+  ]
+
+  if (is_android) {
+    deps += [ "//components/about_ui/android:about_ui_jni_headers" ]
+  }
+}
diff --git a/components/about_ui/DEPS b/components/about_ui/DEPS
new file mode 100644
index 0000000..72b42ddc
--- /dev/null
+++ b/components/about_ui/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+components/grit/components_resources.h",
+  "+jni",
+  "+third_party/brotli",
+  "+ui/base",
+]
diff --git a/components/about_ui/android/BUILD.gn b/components/about_ui/android/BUILD.gn
new file mode 100644
index 0000000..7e825471
--- /dev/null
+++ b/components/about_ui/android/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import("//build/config/android/rules.gni")
+
+generate_jni("about_ui_jni_headers") {
+  sources = [
+    "java/src/org/chromium/components/aboutui/CreditUtils.java",
+  ]
+  jni_package = "components/about_ui"
+}
+
+android_library("aboutui_java") {
+  java_files = [ "java/src/org/chromium/components/aboutui/CreditUtils.java" ]
+  deps = [
+    "//base:base_java",
+  ]
+}
diff --git a/components/about_ui/android/OWNERS b/components/about_ui/android/OWNERS
new file mode 100644
index 0000000..d99c934
--- /dev/null
+++ b/components/about_ui/android/OWNERS
@@ -0,0 +1,2 @@
+agrieve@chromium.org
+torne@chromium.org
diff --git a/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java b/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java
new file mode 100644
index 0000000..b2be4ad
--- /dev/null
+++ b/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.aboutui;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/** Credits-related utilities. */
+@JNINamespace("about_ui")
+public class CreditUtils {
+    private CreditUtils() {}
+
+    /** Returns a string containing the content of about_credits.html. */
+    public static native byte[] nativeGetJavaWrapperCredits();
+}
diff --git a/components/about_ui/credit_utils.cc b/components/about_ui/credit_utils.cc
new file mode 100644
index 0000000..238512d6
--- /dev/null
+++ b/components/about_ui/credit_utils.cc
@@ -0,0 +1,71 @@
+// 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/about_ui/credit_utils.h"
+
+#include <stdint.h>
+
+#include "base/strings/string_piece.h"
+#include "components/grit/components_resources.h"
+#include "third_party/brotli/include/brotli/decode.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_array.h"
+#include "jni/CreditUtils_jni.h"
+#endif
+
+namespace about_ui {
+
+std::string GetCredits(bool include_scripts) {
+  std::string response;
+  base::StringPiece raw_response =
+      ResourceBundle::GetSharedInstance().GetRawDataResource(
+          IDR_ABOUT_UI_CREDITS_HTML);
+  const uint8_t* next_encoded_byte =
+      reinterpret_cast<const uint8_t*>(raw_response.data());
+  size_t input_size_remaining = raw_response.size();
+  BrotliDecoderState* decoder = BrotliDecoderCreateInstance(
+      nullptr /* no custom allocator */, nullptr /* no custom deallocator */,
+      nullptr /* no custom memory handle */);
+  CHECK(!!decoder);
+  while (!BrotliDecoderIsFinished(decoder)) {
+    size_t output_size_remaining = 0;
+    CHECK(BrotliDecoderDecompressStream(
+              decoder, &input_size_remaining, &next_encoded_byte,
+              &output_size_remaining, nullptr,
+              nullptr) != BROTLI_DECODER_RESULT_ERROR);
+    const uint8_t* output_buffer =
+        BrotliDecoderTakeOutput(decoder, &output_size_remaining);
+    response.insert(response.end(), output_buffer,
+                    output_buffer + output_size_remaining);
+  }
+  BrotliDecoderDestroyInstance(decoder);
+  if (include_scripts) {
+    response +=
+        "\n<script src=\"chrome://resources/js/cr.js\"></script>\n"
+        "<script src=\"chrome://credits/credits.js\"></script>\n";
+  }
+  response += "</body>\n</html>";
+  return response;
+}
+
+#if defined(OS_ANDROID)
+static base::android::ScopedJavaLocalRef<jbyteArray> GetJavaWrapperCredits(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& clazz) {
+  std::string html_content = GetCredits(false);
+  const char* html_content_arr = html_content.c_str();
+  return base::android::ToJavaByteArray(
+      env, reinterpret_cast<const uint8_t*>(html_content_arr),
+      html_content.size());
+}
+
+// The RegisterNativesImpl is a static function, so has to be called somewhere.
+bool RegisterAboutUIUtils(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+#endif
+
+}  // namespace about_ui
diff --git a/components/about_ui/credit_utils.h b/components/about_ui/credit_utils.h
new file mode 100644
index 0000000..2bc9345
--- /dev/null
+++ b/components/about_ui/credit_utils.h
@@ -0,0 +1,17 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_
+#define COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_
+
+#include <string>
+
+namespace about_ui {
+
+// Decode a Brotli compressed HTML license file and attach .js files.
+std::string GetCredits(bool include_scripts);
+
+}  // namespace about_ui
+
+#endif  // COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_
diff --git a/components/about_ui/resources/about_credits.js b/components/about_ui/resources/about_credits.js
index d01ffb6..3339a7e 100644
--- a/components/about_ui/resources/about_credits.js
+++ b/components/about_ui/resources/about_credits.js
@@ -2,56 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/* eslint-disable no-restricted-properties */
 function $(id) { return document.getElementById(id); }
-
-function toggle(o) {
-  var licence = o.nextSibling;
-
-  while (licence.className != 'licence') {
-    if (!licence) return false;
-    licence = licence.nextSibling;
-  }
-
-  if (licence.style && licence.style.display == 'block') {
-    licence.style.display = 'none';
-    o.textContent = 'show license';
-  } else {
-    licence.style.display = 'block';
-    o.textContent = 'hide license';
-  }
-  return false;
-}
+/* eslint-enable no-restricted-properties */
 
 document.addEventListener('DOMContentLoaded', function() {
-  var licenseEls = [].slice.call(document.getElementsByClassName('product'));
-
-  licenseEls.sort(function(a, b) {
-    var nameA = a.getElementsByClassName('title')[0].textContent;
-    var nameB = b.getElementsByClassName('title')[0].textContent;
-    if (nameA < nameB) return -1;
-    if (nameA > nameB) return 1;
-    return 0;
-  });
-
-  var parentEl = licenseEls[0].parentNode;
-  parentEl.innerHTML = '';
-  for (var i = 0; i < licenseEls.length; i++) {
-    parentEl.appendChild(licenseEls[i]);
-  }
-
-  document.body.hidden = false;
-
   if (cr.isChromeOS) {
     var keyboardUtils = document.createElement('script');
     keyboardUtils.src = 'chrome://credits/keyboard_utils.js';
     document.body.appendChild(keyboardUtils);
   }
 
-  var links = document.querySelectorAll('a.show');
-  for (var i = 0; i < links.length; ++i) {
-    links[i].onclick = function() { return toggle(this); };
-  }
-
+  $('print-link').hidden = false;
   $('print-link').onclick = function() {
     window.print();
     return false;
diff --git a/components/about_ui/resources/about_credits.tmpl b/components/about_ui/resources/about_credits.tmpl
index fc4fc965..5aa5c9b 100644
--- a/components/about_ui/resources/about_credits.tmpl
+++ b/components/about_ui/resources/about_credits.tmpl
@@ -2,6 +2,7 @@
 <html>
 <head>
 <meta charset="utf-8">
+<meta name="viewport" content="width=device-width">
 <title>Credits</title>
 <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
 <style>
@@ -28,17 +29,20 @@
   margin: 3px;
 }
 .product .homepage {
+  color: blue;
   float: right;
   margin: 3px;
   text-align: right;
 }
-.product .homepage::after {
+.product .homepage::before {
   content: " - ";
 }
 .product .show {
+  color: blue;
   float: right;
   margin: 3px;
   text-align: right;
+  text-decoration: underline;
 }
 .licence {
   background-color: #e8eef7;
@@ -57,15 +61,23 @@
 .dialog .homepage {
   display: none;
 }
+input + label + div {
+  display: none;
+}
+input + label::after {
+  content: "show license";
+}
+input:checked + label + div {
+  display: block;
+}
+input:checked + label::after {
+  content: "hide license";
+}
 </style>
 </head>
-<body hidden>
+<body>
 <span class="page-title" style="float:left;">Credits</span>
-<a id="print-link" href="#" style="float:right;">Print</a>
+<a id="print-link" href="#" style="float:right;" hidden>Print</a>
 <div style="clear:both; overflow:auto;"><!-- Chromium <3s the following projects -->
 {{entries}}
 </div>
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://credits/credits.js"></script>
-</body>
-</html>
\ No newline at end of file
diff --git a/components/about_ui/resources/about_credits_entry.tmpl b/components/about_ui/resources/about_credits_entry.tmpl
index d1810cd..51aa468 100644
--- a/components/about_ui/resources/about_credits_entry.tmpl
+++ b/components/about_ui/resources/about_credits_entry.tmpl
@@ -1,9 +1,9 @@
 <div class="product">
 <span class="title">{{name}}</span>
-<a class="show" href="#">show license</a>
 <span class="homepage"><a href="{{url}}">homepage</a></span>
+<input type="checkbox" hidden id="{{id}}">
+<label class="show" for="{{id}}"></label>
 <div class="licence">
 <pre>{{license}}</pre>
 </div>
 </div>
-
diff --git a/components/cronet/android/api/src/org/chromium/net/QuicException.java b/components/cronet/android/api/src/org/chromium/net/QuicException.java
index b302cee..e3754afd 100644
--- a/components/cronet/android/api/src/org/chromium/net/QuicException.java
+++ b/components/cronet/android/api/src/org/chromium/net/QuicException.java
@@ -28,7 +28,7 @@
     /**
      * Returns the <a href="https://www.chromium.org/quic">QUIC</a> error code, which is a value
      * from <a
-     * href=https://cs.chromium.org/chromium/src/net/quic/quic_protocol.h?type=cs&q=%22enum+QuicErrorCode+%7B%22+file:src/net/quic/quic_protocol.h>
+     * href=https://cs.chromium.org/chromium/src/net/quic/core/quic_error_codes.h?type=cs&q=%22enum+QuicErrorCode+%7B%22>
      * QuicErrorCode</a>.
      */
     public abstract int getQuicDetailedErrorCode();
diff --git a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
index d9d959ae..8915fbe 100644
--- a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
+++ b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
@@ -35,6 +35,10 @@
 // each message gets 3 attempts: the first one, and 2 retries.
 const int kMaxNumberOfRetryAttempts = 2;
 
+// The time to wait in seconds for the server to send its connection response.
+// If not received within this time, the connection will fail.
+const int kConnectionResponseTimeoutSeconds = 2;
+
 }  // namespace
 
 // static
@@ -76,7 +80,12 @@
     BluetoothThrottler* bluetooth_throttler) {
   return base::MakeUnique<BluetoothLowEnergyWeaveClientConnection>(
       remote_device, device_address, adapter, remote_service_uuid,
-      bluetooth_throttler);
+      bluetooth_throttler, base::MakeUnique<TimerFactory>());
+}
+
+std::unique_ptr<base::Timer>
+BluetoothLowEnergyWeaveClientConnection::TimerFactory::CreateTimer() {
+  return base::MakeUnique<base::OneShotTimer>();
 }
 
 BluetoothLowEnergyWeaveClientConnection::
@@ -85,7 +94,8 @@
         const std::string& device_address,
         scoped_refptr<device::BluetoothAdapter> adapter,
         const device::BluetoothUUID remote_service_uuid,
-        BluetoothThrottler* bluetooth_throttler)
+        BluetoothThrottler* bluetooth_throttler,
+        std::unique_ptr<TimerFactory> timer_factory)
     : Connection(device),
       device_address_(device_address),
       adapter_(adapter),
@@ -98,6 +108,7 @@
       tx_characteristic_({device::BluetoothUUID(kTXCharacteristicUUID), ""}),
       rx_characteristic_({device::BluetoothUUID(kRXCharacteristicUUID), ""}),
       bluetooth_throttler_(bluetooth_throttler),
+      timer_factory_(std::move(timer_factory)),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
       sub_status_(SubStatus::DISCONNECTED),
       write_remote_characteristic_pending_(false),
@@ -346,6 +357,7 @@
 void BluetoothLowEnergyWeaveClientConnection::CompleteConnection() {
   PA_LOG(INFO) << "Connection completed. Time elapsed: "
                << base::TimeTicks::Now() - start_time_;
+  connection_response_timer_->Stop();
   SetSubStatus(SubStatus::CONNECTED);
 }
 
@@ -489,6 +501,13 @@
   if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
     PA_LOG(INFO) << "Sending connection request to the server";
     SetSubStatus(SubStatus::WAITING_CONNECTION_RESPONSE);
+    connection_response_timer_ = timer_factory_->CreateTimer();
+    connection_response_timer_->Start(
+        FROM_HERE,
+        base::TimeDelta::FromSeconds(kConnectionResponseTimeoutSeconds),
+        base::Bind(&BluetoothLowEnergyWeaveClientConnection::
+                       OnConnectionResponseTimeout,
+                   weak_ptr_factory_.GetWeakPtr()));
 
     WriteRequest write_request(packet_generator_->CreateConnectionRequest(),
                                WriteRequestType::CONNECTION_REQUEST);
@@ -615,6 +634,13 @@
   PA_LOG(INFO) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
 }
 
+void BluetoothLowEnergyWeaveClientConnection::OnConnectionResponseTimeout() {
+  DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE);
+  PA_LOG(ERROR) << "Timed out waiting for connection response. Closing "
+                << "connection.";
+  DestroyConnection();
+}
+
 std::string BluetoothLowEnergyWeaveClientConnection::GetDeviceAddress() {
   // When the remote device is connected we should rely on the address given by
   // |gatt_connection_|. As the device address may change if the device is
diff --git a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
index e86990c4..1ce1474 100644
--- a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
+++ b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
@@ -17,6 +17,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
 #include "components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h"
 #include "components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h"
@@ -78,6 +79,11 @@
     static Factory* factory_instance_;
   };
 
+  class TimerFactory {
+   public:
+    virtual std::unique_ptr<base::Timer> CreateTimer();
+  };
+
   // The sub-state of a BluetoothLowEnergyWeaveClientConnection
   // extends the IN_PROGRESS state of Connection::Status.
   enum SubStatus {
@@ -94,7 +100,7 @@
 
   // Constructs a Bluetooth low energy connection to the service with
   // |remote_service_| on the |remote_device|. The |adapter| must be already
-  // initialized and ready. The GATT connection may alreaady be established and
+  // initialized and ready. The GATT connection may already be established and
   // pass through |gatt_connection|. A subsequent call to Connect() must be
   // made.
   BluetoothLowEnergyWeaveClientConnection(
@@ -102,7 +108,8 @@
       const std::string& device_address,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
-      BluetoothThrottler* bluetooth_throttler);
+      BluetoothThrottler* bluetooth_throttler,
+      std::unique_ptr<TimerFactory> timer_factory);
 
   ~BluetoothLowEnergyWeaveClientConnection() override;
 
@@ -252,6 +259,9 @@
   // Prints the time elapsed since |Connect()| was called.
   void PrintTimeElapsed();
 
+  // Called when waiting for connection response from server times out.
+  void OnConnectionResponseTimeout();
+
   // Returns the service corresponding to |remote_service_| in the current
   // device.
   device::BluetoothRemoteGattService* GetRemoteService();
@@ -294,6 +304,9 @@
   // workaround for crbug.com/508919. Not owned, must outlive this instance.
   BluetoothThrottler* bluetooth_throttler_;
 
+  // Used for timing out when waiting for connection response from the server.
+  std::unique_ptr<TimerFactory> timer_factory_;
+
   scoped_refptr<base::TaskRunner> task_runner_;
 
   // The GATT connection with the remote device.
@@ -319,9 +332,12 @@
 
   std::queue<WriteRequest> write_requests_queue_;
 
-  // Stores when the instace was created.
+  // Stores when the instance was created.
   base::TimeTicks start_time_;
 
+  // Used for timing out when waiting for connection response from the server.
+  std::unique_ptr<base::Timer> connection_response_timer_;
+
   base::WeakPtrFactory<BluetoothLowEnergyWeaveClientConnection>
       weak_ptr_factory_;
 
diff --git a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
index 894860c..fe23252 100644
--- a/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
+++ b/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
@@ -14,6 +14,8 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
+#include "base/timer/mock_timer.h"
+#include "base/timer/timer.h"
 #include "components/cryptauth/bluetooth_throttler.h"
 #include "components/cryptauth/connection_finder.h"
 #include "components/cryptauth/connection_observer.h"
@@ -311,6 +313,21 @@
   MockBluetoothLowEnergyWeavePacketReceiver* most_recent_instance_;
 };
 
+class TestTimerFactory
+    : public BluetoothLowEnergyWeaveClientConnection::TimerFactory {
+ public:
+  std::unique_ptr<base::Timer> CreateTimer() override {
+    last_created_timer_ = new base::MockTimer(false /* retains_user_task */,
+                                              false /* is_repeating */);
+    return base::WrapUnique(last_created_timer_);
+  }
+
+  base::MockTimer* last_created_timer() { return last_created_timer_; }
+
+ private:
+  base::MockTimer* last_created_timer_;
+};
+
 class TestBluetoothLowEnergyWeaveClientConnection
     : public BluetoothLowEnergyWeaveClientConnection {
  public:
@@ -319,12 +336,15 @@
       const std::string& device_address,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
-      BluetoothThrottler* bluetooth_throttler)
+      BluetoothThrottler* bluetooth_throttler,
+      std::unique_ptr<BluetoothLowEnergyWeaveClientConnection::TimerFactory>
+          timer_factory)
       : BluetoothLowEnergyWeaveClientConnection(remote_device,
                                                 device_address,
                                                 adapter,
                                                 remote_service_uuid,
-                                                bluetooth_throttler) {}
+                                                bluetooth_throttler,
+                                                std::move(timer_factory)) {}
 
   ~TestBluetoothLowEnergyWeaveClientConnection() override {}
 
@@ -363,6 +383,7 @@
         rx_characteristic_uuid_(device::BluetoothUUID(kRXCharacteristicUUID)),
         notify_session_alias_(NULL),
         bluetooth_throttler_(new NiceMock<MockBluetoothThrottler>),
+        test_timer_factory_(new TestTimerFactory()),
         task_runner_(new base::TestSimpleTaskRunner),
         generator_factory_(
             new MockBluetoothLowEnergyWeavePacketGeneratorFactory()),
@@ -426,7 +447,8 @@
     std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
         new TestBluetoothLowEnergyWeaveClientConnection(
             remote_device_, kTestRemoteDeviceBluetoothAddress, adapter_,
-            service_uuid_, bluetooth_throttler_.get()));
+            service_uuid_, bluetooth_throttler_.get(),
+            base::WrapUnique(test_timer_factory_)));
 
     EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
     EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
@@ -624,6 +646,7 @@
   std::vector<uint8_t> last_value_written_on_tx_characteristic_;
   device::MockBluetoothGattNotifySession* notify_session_alias_;
   std::unique_ptr<MockBluetoothThrottler> bluetooth_throttler_;
+  TestTimerFactory* test_timer_factory_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::MessageLoop message_loop_;
   bool last_wire_message_success_;
@@ -662,7 +685,8 @@
        CreateAndDestroyWithoutConnectCallDoesntCrash) {
   BluetoothLowEnergyWeaveClientConnection connection(
       remote_device_, kTestRemoteDeviceBluetoothAddress, adapter_,
-      service_uuid_, bluetooth_throttler_.get());
+      service_uuid_, bluetooth_throttler_.get(),
+      base::WrapUnique(test_timer_factory_));
 }
 
 TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
@@ -1162,6 +1186,20 @@
   ConnectionResponseReceived(connection.get(), kDefaultMaxPacketSize);
 }
 
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+       ConnectionResponseTimeout) {
+  std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+      CreateConnection());
+  ConnectGatt(connection.get());
+  CharacteristicsFound(connection.get());
+  NotifySessionStarted(connection.get());
+
+  test_timer_factory_->last_created_timer()->Fire();
+
+  EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+  EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
 }  // namespace weave
 
 }  // namespace cryptauth
diff --git a/components/neterror/resources/neterror.html b/components/neterror/resources/neterror.html
index 3851e4fe..3038e717 100644
--- a/components/neterror/resources/neterror.html
+++ b/components/neterror/resources/neterror.html
@@ -6,9 +6,9 @@
                                  maximum-scale=1.0, user-scalable=no">
   <title i18n-content="title"></title>
   <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_common.css">
-  <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_v2.css">
+  <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_large.css">
   <link rel="stylesheet" href="neterror.css">
-  <script src="../../../components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js"></script>
+  <script src="../../../components/security_interstitials/core/browser/resources/interstitial_mobile_nav.js"></script>
   <script src="neterror.js"></script>
   <script src="offline.js"></script>
 </head>
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index 9dfab58bf..d3eeb441 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -22,10 +22,10 @@
 namespace offline_pages {
 
 const base::Feature kOfflineBookmarksFeature{"OfflineBookmarks",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kOffliningRecentPagesFeature{
-    "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT};
+    "OfflineRecentPages", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/offline_pages/core/offline_page_model_impl_unittest.cc b/components/offline_pages/core/offline_page_model_impl_unittest.cc
index 5e70978..c50c7db 100644
--- a/components/offline_pages/core/offline_page_model_impl_unittest.cc
+++ b/components/offline_pages/core/offline_page_model_impl_unittest.cc
@@ -1297,50 +1297,32 @@
 }
 
 TEST(CommandLineFlagsTest, OfflineBookmarks) {
-  // Disabled by default.
-  EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(kOfflineBookmarksFeature);
+  // Enabled by default.
   EXPECT_TRUE(offline_pages::IsOfflineBookmarksEnabled());
+
+  // Check if feature is correctly disabled by command-line flag.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kOfflineBookmarksFeature);
+  EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
 }
 
 TEST(CommandLineFlagsTest, OffliningRecentPages) {
-  // Enable offline bookmarks feature first.
-  // TODO(dimich): once offline pages are enabled by default, remove this.
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
-      new base::test::ScopedFeatureList);
-  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
-
-  // This feature is still disabled by default.
-  EXPECT_FALSE(offline_pages::IsOffliningRecentPagesEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  scoped_feature_list.reset(new base::test::ScopedFeatureList);
-  scoped_feature_list->InitFromCommandLine(
-      std::string(kOfflineBookmarksFeature.name) + "," +
-          kOffliningRecentPagesFeature.name,
-      "");
+  // Enabled by default.
   EXPECT_TRUE(offline_pages::IsOffliningRecentPagesEnabled());
+
+  // Check if feature is correctly disabled by command-line flag.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kOffliningRecentPagesFeature);
+  EXPECT_FALSE(offline_pages::IsOffliningRecentPagesEnabled());
 }
 
 TEST(CommandLineFlagsTest, OfflinePagesSharing) {
-  // Enable offline bookmarks feature first.
-  // TODO(dimich): once offline pages are enabled by default, remove this.
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
-      new base::test::ScopedFeatureList);
-  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
-
-  // This feature is still disabled by default.
+  // This feature is disabled by default.
   EXPECT_FALSE(offline_pages::IsOfflinePagesSharingEnabled());
 
-  // Check if feature is correctly enabled by command-line flag.
-  scoped_feature_list.reset(new base::test::ScopedFeatureList);
-  scoped_feature_list->InitFromCommandLine(
-      std::string(kOfflineBookmarksFeature.name) + "," +
-          kOfflinePagesSharingFeature.name,
-      "");
+  // Check if feature is correctly disabled by command-line flag.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kOfflinePagesSharingFeature);
   EXPECT_TRUE(offline_pages::IsOfflinePagesSharingEnabled());
 }
 
diff --git a/components/resources/security_interstitials_resources.grdp b/components/resources/security_interstitials_resources.grdp
index 2fb0f05c..4d72526b 100644
--- a/components/resources/security_interstitials_resources.grdp
+++ b/components/resources/security_interstitials_resources.grdp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <include name="IDR_SECURITY_INTERSTITIAL_UI_HTML" file="../security_interstitials/core/browser/resources/interstitial_ui.html" flattenhtml="true" type="BINDATA" />
-  <include name="IDR_SECURITY_INTERSTITIAL_HTML" file="../security_interstitials/core/browser/resources/interstitial_v2.html" flattenhtml="true" type="BINDATA" />
+  <include name="IDR_SECURITY_INTERSTITIAL_UI_HTML" file="../security_interstitials/core/browser/resources/list_of_interstitials.html" flattenhtml="true" type="BINDATA" />
+  <include name="IDR_SECURITY_INTERSTITIAL_HTML" file="../security_interstitials/core/browser/resources/interstitial_large.html" flattenhtml="true" type="BINDATA" />
   <include name="IDR_SECURITY_INTERSTITIAL_QUIET_HTML" file="../security_interstitials/core/browser/resources/interstitial_webview_quiet.html" flattenhtml="true" type="BINDATA" />
-</grit-part>
\ No newline at end of file
+</grit-part>
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.css b/components/security_interstitials/core/browser/resources/interstitial_large.css
similarity index 99%
rename from components/security_interstitials/core/browser/resources/interstitial_v2.css
rename to components/security_interstitials/core/browser/resources/interstitial_large.css
index fdda65d..6ca9c0c 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.css
@@ -14,7 +14,6 @@
 }
 
 button {
-  -webkit-user-select: none;
   background: rgb(66, 133, 244);
   border: 0;
   border-radius: 2px;
@@ -26,6 +25,7 @@
   margin: 0;
   padding: 10px 24px;
   transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
+  user-select: none;
 }
 
 [dir='rtl'] button {
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.html b/components/security_interstitials/core/browser/resources/interstitial_large.html
similarity index 91%
rename from components/security_interstitials/core/browser/resources/interstitial_v2.html
rename to components/security_interstitials/core/browser/resources/interstitial_large.html
index f382689f..bcb762a 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.html
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.html
@@ -6,14 +6,14 @@
       content="initial-scale=1, minimum-scale=1, width=device-width">
   <title i18n-content="tabTitle"></title>
   <link rel="stylesheet" href="interstitial_common.css">
-  <link rel="stylesheet" href="interstitial_v2.css">
+  <link rel="stylesheet" href="interstitial_large.css">
   <script src="../../../../../ui/webui/resources/js/util.js"></script>
   <script src="captive_portal.js"></script>
   <script src="ssl.js"></script>
   <script src="extended_reporting.js"></script>
-  <script src="interstitial_v2_mobile.js"></script>
+  <script src="interstitial_mobile_nav.js"></script>
   <script src="interstitial_common.js"></script>
-  <script src="interstitial_v2.js"></script>
+  <script src="interstitial_large.js"></script>
 </head>
 <body id="body">
   <div class="interstitial-wrapper">
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.js b/components/security_interstitials/core/browser/resources/interstitial_large.js
similarity index 100%
rename from components/security_interstitials/core/browser/resources/interstitial_v2.js
rename to components/security_interstitials/core/browser/resources/interstitial_large.js
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js b/components/security_interstitials/core/browser/resources/interstitial_mobile_nav.js
similarity index 100%
rename from components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
rename to components/security_interstitials/core/browser/resources/interstitial_mobile_nav.js
diff --git a/components/security_interstitials/core/browser/resources/interstitial_ui.html b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
similarity index 100%
rename from components/security_interstitials/core/browser/resources/interstitial_ui.html
rename to components/security_interstitials/core/browser/resources/list_of_interstitials.html
diff --git a/content/browser/appcache/appcache_manifest_parser.cc b/content/browser/appcache/appcache_manifest_parser.cc
index 8189858a..ca4cb7a 100644
--- a/content/browser/appcache/appcache_manifest_parser.cc
+++ b/content/browser/appcache/appcache_manifest_parser.cc
@@ -37,6 +37,7 @@
 #include "base/i18n/icu_string_conversions.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "url/gurl.h"
 
@@ -57,8 +58,14 @@
   return annotation == L"isPattern";
 }
 
+bool ScopeMatches(const GURL& manifest_url, const GURL& namespace_url) {
+  return base::StartsWith(namespace_url.spec(),
+                          manifest_url.Resolve(".").spec(),
+                          base::CompareCase::SENSITIVE);
 }
 
+}  // namespace
+
 enum Mode {
   EXPLICIT,
   INTERCEPT,
@@ -73,10 +80,7 @@
   UNKNOWN_VERB,
 };
 
-AppCacheManifest::AppCacheManifest()
-    : online_whitelist_all(false),
-      did_ignore_intercept_namespaces(false) {
-}
+AppCacheManifest::AppCacheManifest() {}
 
 AppCacheManifest::~AppCacheManifest() {}
 
@@ -99,6 +103,7 @@
   DCHECK(manifest.online_whitelist_namespaces.empty());
   DCHECK(!manifest.online_whitelist_all);
   DCHECK(!manifest.did_ignore_intercept_namespaces);
+  DCHECK(!manifest.did_ignore_fallback_namespaces);
 
   Mode mode = EXPLICIT;
 
@@ -223,7 +228,7 @@
                 is_pattern));
       }
     } else if (mode == INTERCEPT) {
-      if (parse_mode != PARSE_MANIFEST_ALLOWING_INTERCEPTS) {
+      if (parse_mode != PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES) {
         manifest.did_ignore_intercept_namespaces = true;
         continue;
       }
@@ -337,6 +342,13 @@
         continue;
       }
 
+      if (parse_mode != PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES) {
+        if (!ScopeMatches(manifest_url, namespace_url)) {
+          manifest.did_ignore_fallback_namespaces = true;
+          continue;
+        }
+      }
+
       // Skip whitespace separating fallback namespace from URL.
       while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
         ++line_p;
diff --git a/content/browser/appcache/appcache_manifest_parser.h b/content/browser/appcache/appcache_manifest_parser.h
index 19685f8..75f7fe8 100644
--- a/content/browser/appcache/appcache_manifest_parser.h
+++ b/content/browser/appcache/appcache_manifest_parser.h
@@ -51,13 +51,14 @@
   AppCacheNamespaceVector intercept_namespaces;
   AppCacheNamespaceVector fallback_namespaces;
   AppCacheNamespaceVector online_whitelist_namespaces;
-  bool online_whitelist_all;
-  bool did_ignore_intercept_namespaces;
+  bool online_whitelist_all = false;
+  bool did_ignore_intercept_namespaces = false;
+  bool did_ignore_fallback_namespaces = false;
 };
 
 enum ParseMode {
   PARSE_MANIFEST_PER_STANDARD,
-  PARSE_MANIFEST_ALLOWING_INTERCEPTS
+  PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES
 };
 
 CONTENT_EXPORT bool ParseManifest(
diff --git a/content/browser/appcache/appcache_manifest_parser_unittest.cc b/content/browser/appcache/appcache_manifest_parser_unittest.cc
index 4f3d2f4c..bd34ef19 100644
--- a/content/browser/appcache/appcache_manifest_parser_unittest.cc
+++ b/content/browser/appcache/appcache_manifest_parser_unittest.cc
@@ -19,10 +19,11 @@
 TEST(AppCacheManifestParserTest, NoData) {
   GURL url;
   AppCacheManifest manifest;
-  EXPECT_FALSE(ParseManifest(url, "", 0,
-                             PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+  EXPECT_FALSE(ParseManifest(
+      url, "", 0, PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES, manifest));
   EXPECT_FALSE(ParseManifest(url, "CACHE MANIFEST\r", 0,  // Len is 0.
-                             PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                             PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                             manifest));
 }
 
 TEST(AppCacheManifestParserTest, CheckSignature) {
@@ -43,7 +44,8 @@
   for (size_t i = 0; i < arraysize(kBadSignatures); ++i) {
     const std::string bad = kBadSignatures[i];
     EXPECT_FALSE(ParseManifest(url, bad.c_str(), bad.length(),
-                               PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                               PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                               manifest));
   }
 
   const std::string kGoodSignatures[] = {
@@ -61,7 +63,8 @@
   for (size_t i = 0; i < arraysize(kGoodSignatures); ++i) {
     const std::string good = kGoodSignatures[i];
     EXPECT_TRUE(ParseManifest(url, good.c_str(), good.length(),
-                              PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                              PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                              manifest));
   }
 }
 
@@ -72,7 +75,8 @@
     "http://absolute.com/addme.com");
   const GURL kUrl;
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.explicit_urls.empty());
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
@@ -100,10 +104,13 @@
     "*\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 
   base::hash_set<std::string> urls = manifest.explicit_urls;
   const size_t kExpected = 5;
@@ -116,13 +123,15 @@
   // Wildcard is treated as a relative URL in explicit section.
   EXPECT_TRUE(urls.find("http://www.foo.com/*") != urls.end());
 
-  // We should get the same results with intercepts disallowed.
+  // We should get the same results with dangerous features disallowed.
   manifest = AppCacheManifest();
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
                             PARSE_MANIFEST_PER_STANDARD, manifest));
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 
   urls = manifest.explicit_urls;
   ASSERT_EQ(kExpected, urls.size());
@@ -156,11 +165,14 @@
     "*foo\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.explicit_urls.empty());
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.intercept_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 
   const AppCacheNamespaceVector& online = manifest.online_whitelist_namespaces;
   const size_t kExpected = 6;
@@ -203,10 +215,13 @@
     "http://www.glorp.com/notsame relative/skipped\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.explicit_urls.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 
   const AppCacheNamespaceVector& fallbacks = manifest.fallback_namespaces;
   const size_t kExpected = 5;
@@ -236,8 +251,14 @@
             fallbacks[4].namespace_url);
   EXPECT_EQ(GURL("http://glorp.com/relative/fourfb"),
             fallbacks[4].target_url);
-
   EXPECT_TRUE(manifest.intercept_namespaces.empty());
+
+  // Nothing should be ignored since all namespaces are in scope.
+  manifest = AppCacheManifest();
+  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+                            PARSE_MANIFEST_PER_STANDARD, manifest));
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 }
 
 TEST(AppCacheManifestParserTest, FallbackUrlsWithPort) {
@@ -254,7 +275,8 @@
     "http://www.portme.com:1234/skipme http://www.portme.com/noport\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.explicit_urls.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
@@ -277,8 +299,14 @@
             fallbacks[2].namespace_url);
   EXPECT_EQ(GURL("http://www.portme.com:1234/threefb"),
             fallbacks[2].target_url);
-
   EXPECT_TRUE(manifest.intercept_namespaces.empty());
+
+  // Nothing should be ignored since all namespaces are in scope.
+  manifest = AppCacheManifest();
+  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+                            PARSE_MANIFEST_PER_STANDARD, manifest));
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 }
 
 TEST(AppCacheManifestParserTest, InterceptUrls) {
@@ -297,11 +325,14 @@
     "relative/wrong/again missing/intercept_type\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.explicit_urls.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 
   const AppCacheNamespaceVector& intercepts = manifest.intercept_namespaces;
   const size_t kExpected = 3;
@@ -322,7 +353,7 @@
   EXPECT_EQ(GURL("http://www.portme.com:1234/int3"),
             intercepts[2].target_url);
 
-  // Disallow intercepts ths time.
+  // Disallow intercepts this time.
   manifest = AppCacheManifest();
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
                             PARSE_MANIFEST_PER_STANDARD, manifest));
@@ -331,6 +362,8 @@
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
   EXPECT_TRUE(manifest.intercept_namespaces.empty());
   EXPECT_FALSE(manifest.online_whitelist_all);
+  EXPECT_TRUE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
 }
 
 TEST(AppCacheManifestParserTest, ComboUrls) {
@@ -356,7 +389,8 @@
     "relative/whitelist-3#strip\r"
     "http://combo.com:99/whitelist-4\r");
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.online_whitelist_all);
 
   base::hash_set<std::string> urls = manifest.explicit_urls;
@@ -403,7 +437,8 @@
     "\xC0" "invalidutf8\r"
     "nonbmp" "\xF1\x84\xAB\xBC\r");
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   base::hash_set<std::string> urls = manifest.explicit_urls;
   EXPECT_TRUE(urls.find("http://bad.com/%EF%BF%BDinvalidutf8") != urls.end());
   EXPECT_TRUE(urls.find("http://bad.com/nonbmp%F1%84%AB%BC") != urls.end());
@@ -416,7 +451,8 @@
     "CACHE MANIFEST\r"
     "resource.txt this stuff after the white space should be ignored\r");
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
 
   base::hash_set<std::string> urls = manifest.explicit_urls;
   EXPECT_TRUE(urls.find("http://smorg.borg/resource.txt") != urls.end());
@@ -433,7 +469,8 @@
     "https://www.xyz.com/secureschemedifforigin\r");
 
   EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.fallback_namespaces.empty());
   EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
 
@@ -470,10 +507,12 @@
 
 
   AppCacheManifest manifest;
-  EXPECT_TRUE(ParseManifest(kUrl, kManifestBody.c_str(),
-                            kManifestBody.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+  EXPECT_TRUE(ParseManifest(kUrl, kManifestBody.c_str(), kManifestBody.length(),
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   EXPECT_TRUE(manifest.online_whitelist_all);
+  EXPECT_FALSE(manifest.did_ignore_intercept_namespaces);
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
   EXPECT_EQ(1u, manifest.explicit_urls.size());
   EXPECT_EQ(3u, manifest.intercept_namespaces.size());
   EXPECT_EQ(2u, manifest.fallback_namespaces.size());
@@ -516,4 +555,31 @@
       manifest.online_whitelist_namespaces[1].target_url);
 }
 
+TEST(AppCacheManifestParserTest, IgnoreDangerousFallbacks) {
+  const GURL kUrl("http://foo.com/scope/manifest?with_query_args");
+  const std::string kData(
+      "CACHE MANIFEST\r"
+      "FALLBACK:\r"
+      "http://foo.com/scope/  fallback_url\r"
+      "http://foo.com/out_of_scope/ fallback_url\r");
+
+  // Scope matching depends on resolving "." as a relative url.
+  EXPECT_EQ(kUrl.Resolve(".").spec(), std::string("http://foo.com/scope/"));
+
+  AppCacheManifest manifest;
+  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
+  EXPECT_FALSE(manifest.did_ignore_fallback_namespaces);
+  EXPECT_EQ(2u, manifest.fallback_namespaces.size());
+
+  manifest = AppCacheManifest();
+  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+                            PARSE_MANIFEST_PER_STANDARD, manifest));
+  EXPECT_TRUE(manifest.did_ignore_fallback_namespaces);
+  EXPECT_EQ(1u, manifest.fallback_namespaces.size());
+  EXPECT_EQ(GURL("http://foo.com/scope/"),
+            manifest.fallback_namespaces[0].namespace_url);
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_unittest.cc b/content/browser/appcache/appcache_unittest.cc
index 6bf5d79..42bf790 100644
--- a/content/browser/appcache/appcache_unittest.cc
+++ b/content/browser/appcache/appcache_unittest.cc
@@ -565,7 +565,8 @@
   scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
   AppCacheManifest manifest;
   EXPECT_TRUE(ParseManifest(kManifestUrl, kData.c_str(), kData.length(),
-                            PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+                            PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES,
+                            manifest));
   cache->InitializeWithManifest(&manifest);
   EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE,
             cache->online_whitelist_namespaces_[0].type);
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc
index e4f50ea2..ec16a5a 100644
--- a/content/browser/appcache/appcache_update_job.cc
+++ b/content/browser/appcache/appcache_update_job.cc
@@ -712,9 +712,9 @@
   AppCacheManifest manifest;
   if (!ParseManifest(manifest_url_, manifest_data_.data(),
                      manifest_data_.length(),
-                     manifest_has_valid_mime_type_ ?
-                        PARSE_MANIFEST_ALLOWING_INTERCEPTS :
-                        PARSE_MANIFEST_PER_STANDARD,
+                     manifest_has_valid_mime_type_
+                         ? PARSE_MANIFEST_ALLOWING_DANGEROUS_FEATURES
+                         : PARSE_MANIFEST_PER_STANDARD,
                      manifest)) {
     const char kFormatString[] = "Failed to parse manifest %s";
     const std::string message = base::StringPrintf(kFormatString,
@@ -746,13 +746,20 @@
     }
   }
 
+  // Warn about dangerous features being ignored due to the wrong content-type
+  // Must be done after associating all pending master hosts.
   if (manifest.did_ignore_intercept_namespaces) {
-    // Must be done after associating all pending master hosts.
     std::string message(
         "Ignoring the INTERCEPT section of the application cache manifest "
         "because the content type is not text/cache-manifest");
     LogConsoleMessageToAll(message);
   }
+  if (manifest.did_ignore_fallback_namespaces) {
+    std::string message(
+        "Ignoring out of scope FALLBACK entries of the application cache "
+        "manifest because the content-type is not text/cache-manifest");
+    LogConsoleMessageToAll(message);
+  }
 
   group_->SetUpdateAppCacheStatus(AppCacheGroup::DOWNLOADING);
   NotifyAllAssociatedHosts(APPCACHE_DOWNLOADING_EVENT);
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 4bd9d50..d8ffc005 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -1079,6 +1079,31 @@
   }
 }
 
+// Checks that there's no UAF if NavigationHandleImpl::WillStartRequest cancels
+// the navigation.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
+                       CancelNavigationInWillStartRequest) {
+  const GURL kUrl1 = embedded_test_server()->GetURL("/title1.html");
+  const GURL kUrl2 = embedded_test_server()->GetURL("/title2.html");
+  // First make a successful commit, as this issue only reproduces when there
+  // are existing entries (i.e. when NavigationControllerImpl::GetVisibleEntry
+  // has safe_to_show_pending=false).
+  EXPECT_TRUE(NavigateToURL(shell(), kUrl1));
+
+  // To take the path that doesn't run beforeunload, so that
+  // NavigationControllerImpl::NavigateToPendingEntry is on the botttom of the
+  // stack when NavigationHandleImpl::WillStartRequest is called.
+  CrashTab(shell()->web_contents());
+
+  // Set up a NavigationThrottle that will cancel the navigation in
+  // WillStartRequest.
+  TestNavigationThrottleInstaller installer(
+      shell()->web_contents(), NavigationThrottle::CANCEL_AND_IGNORE,
+      NavigationThrottle::PROCEED, NavigationThrottle::PROCEED);
+
+  EXPECT_FALSE(NavigateToURL(shell(), kUrl2));
+}
+
 // Specialized test that verifies the NavigationHandle gets the HTTPS upgraded
 // URL from the very beginning of the navigation.
 class NavigationHandleImplHttpsUpgradeBrowserTest
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index dee3a58..521b288 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -28,6 +28,7 @@
 #include "content/common/appcache_interfaces.h"
 #include "content/common/resource_request_body_impl.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/navigation_controller.h"
@@ -322,7 +323,8 @@
       bindings_(NavigationEntryImpl::kInvalidBindings),
       response_should_be_rendered_(true),
       associated_site_instance_type_(AssociatedSiteInstanceType::NONE),
-      may_transfer_(may_transfer) {
+      may_transfer_(may_transfer),
+      weak_factory_(this) {
   DCHECK(!browser_initiated || (entry != nullptr && frame_entry != nullptr));
   TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationRequest", this,
                            "frame_tree_node",
@@ -738,21 +740,26 @@
 
   if (on_start_checks_complete_closure_)
     on_start_checks_complete_closure_.Run();
-
   // Abort the request if needed. This will destroy the NavigationRequest.
   if (result == NavigationThrottle::CANCEL_AND_IGNORE ||
-      result == NavigationThrottle::CANCEL) {
-    // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
-    OnRequestFailed(false, net::ERR_ABORTED);
-
-    // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
-    // destroyed the NavigationRequest.
-    return;
-  }
-
-  if (result == NavigationThrottle::BLOCK_REQUEST ||
+      result == NavigationThrottle::CANCEL ||
+      result == NavigationThrottle::BLOCK_REQUEST ||
       result == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE) {
-    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT);
+    // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
+    int error_code = net::ERR_ABORTED;
+    if (result == NavigationThrottle::BLOCK_REQUEST ||
+        result == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE) {
+      error_code = net::ERR_BLOCKED_BY_CLIENT;
+    }
+
+    // If the start checks completed synchronously, which could happen if there
+    // is no onbeforeunload handler or if a NavigationThrottle cancelled it,
+    // then this could cause reentrancy into NavigationController. So use a
+    // PostTask to avoid that.
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&NavigationRequest::OnRequestFailed,
+                   weak_factory_.GetWeakPtr(), false, error_code));
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 9024de2..7e90d70 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/common/content_export.h"
@@ -285,6 +286,8 @@
 
   base::Closure on_start_checks_complete_closure_;
 
+  base::WeakPtrFactory<NavigationRequest> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
 };
 
diff --git a/content/browser/web_contents/aura/shadow_layer_delegate.cc b/content/browser/web_contents/aura/shadow_layer_delegate.cc
index 84ef3f68..07be1c2 100644
--- a/content/browser/web_contents/aura/shadow_layer_delegate.cc
+++ b/content/browser/web_contents/aura/shadow_layer_delegate.cc
@@ -45,9 +45,9 @@
   gfx::Rect paint_rect = gfx::Rect(0, 0, kShadowThick,
                                    layer_->bounds().height());
   cc::PaintFlags flags;
-  flags.setShader(cc::WrapSkShader(SkGradientShader::MakeLinear(
-      points, kShadowColors, NULL, arraysize(points),
-      SkShader::kRepeat_TileMode)));
+  flags.setShader(cc::PaintShader::MakeLinearGradient(
+      points, kShadowColors, nullptr, arraysize(points),
+      SkShader::kRepeat_TileMode));
   ui::PaintRecorder recorder(context, layer_->size());
   recorder.canvas()->DrawRect(paint_rect, flags);
 }
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 16abd8c9..5a261cae 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -606,6 +606,7 @@
     {"colorSuggestionPicker.css", IDR_COLOR_SUGGESTION_PICKER_CSS,
      ui::SCALE_FACTOR_NONE, true},
 #endif
+    {"placeholderIcon", IDR_PLACEHOLDER_ICON, ui::SCALE_FACTOR_100P, false},
 };
 
 }  // namespace
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index a349241..90e1e69 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -80,6 +80,8 @@
 
 #if defined(OS_MACOSX)
 #include "base/allocator/allocator_interception_mac.h"
+#include "base/process/process.h"
+#include "content/common/mac/app_nap_activity.h"
 #endif
 
 using tracked_objects::ThreadData;
@@ -563,6 +565,10 @@
           switches::kEnableHeapProfiling)) {
     base::allocator::PeriodicallyShimNewMallocZones();
   }
+  if (base::Process::IsAppNapEnabled()) {
+    app_nap_activity_.reset(new AppNapActivity());
+    app_nap_activity_->Begin();
+  };
 #endif
 
   message_loop_->task_runner()->PostDelayedTask(
@@ -757,6 +763,15 @@
   if (backgrounded)
     timer_slack = base::TIMER_SLACK_MAXIMUM;
   base::MessageLoop::current()->SetTimerSlack(timer_slack);
+#if defined(OS_MACOSX)
+  if (base::Process::IsAppNapEnabled()) {
+    if (backgrounded) {
+      app_nap_activity_->End();
+    } else {
+      app_nap_activity_->Begin();
+    }
+  }
+#endif  // defined(OS_MACOSX)
 }
 
 void ChildThreadImpl::OnProcessPurgeAndSuspend() {
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index a7e2cd2..ab9a4ec 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -59,6 +59,10 @@
 class ResourceDispatcher;
 class ThreadSafeSender;
 
+#if defined(OS_MACOSX)
+class AppNapActivity;
+#endif
+
 // The main thread of a child process derives from this class.
 class CONTENT_EXPORT ChildThreadImpl
     : public IPC::Listener,
@@ -281,6 +285,10 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> browser_process_io_runner_;
 
+#if defined(OS_MACOSX)
+  std::unique_ptr<AppNapActivity> app_nap_activity_;
+#endif  // defined(OS_MACOSX)
+
   std::unique_ptr<base::WeakPtrFactory<ChildThreadImpl>>
       channel_connected_factory_;
 
diff --git a/content/common/mac/app_nap_activity.h b/content/common/mac/app_nap_activity.h
index c07f9ee8..c2896e8 100644
--- a/content/common/mac/app_nap_activity.h
+++ b/content/common/mac/app_nap_activity.h
@@ -27,6 +27,10 @@
   AppNapActivity();
   ~AppNapActivity();
 
+  // Because there's no NSApplication in renderers, do some housekeeping
+  // to become eligible for App Nap.
+  static void InitializeAppNapSupport();
+
   // Begin an activity and store the provided token.
   void Begin();
 
diff --git a/content/common/mac/app_nap_activity.mm b/content/common/mac/app_nap_activity.mm
index d2c82e8..4fa7b58 100644
--- a/content/common/mac/app_nap_activity.mm
+++ b/content/common/mac/app_nap_activity.mm
@@ -8,6 +8,12 @@
 
 #include "base/mac/scoped_nsobject.h"
 
+extern "C" {
+void __CFRunLoopSetOptionsReason(uint64_t options,
+                                 NSString* reason,
+                                 int unused);
+}
+
 namespace content {
 
 namespace {
@@ -33,6 +39,21 @@
   DCHECK(!assertion_->obj.get());
 };
 
+void AppNapActivity::InitializeAppNapSupport() {
+  // Reason strings are the same as
+  // what macOS sends in the corresponding call.
+  // |options| (argument 1) are magic numbers as found in the
+  // callsites mentioned above.
+  //
+  // Normally happens during launch services check-in. (HIToolbox)
+  __CFRunLoopSetOptionsReason(
+      1, @"Finished checking in as application - waiting for events", 0);
+  // Normally happens in a dispatch_once in the NSApplication event loop.
+  // (CoreFoundation).
+  __CFRunLoopSetOptionsReason(
+      0x3b000000, @"Finished delay after app launch and bundle check", 0);
+}
+
 void AppNapActivity::Begin() {
   DCHECK(!assertion_->obj.get());
   id assertion =
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index 77b2f00a..47bcbfc8 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -316,7 +316,8 @@
   // std::unique_ptr<Foo, BrowserThread::DeleteOnIOThread> ptr;
   //
   // Note: when migrating BrowserThreads to TaskScheduler based
-  // SequencedTaskRunners these map to base::OnTaskRunnerDeleter.
+  // SequencedTaskRunners these map to base::OnTaskRunnerDeleter and
+  // base::RefCountedDeleteOnSequence.
   struct DeleteOnUIThread : public DeleteOnThread<UI> { };
   struct DeleteOnIOThread : public DeleteOnThread<IO> { };
   struct DeleteOnFileThread : public DeleteOnThread<FILE> { };
diff --git a/content/public/test/navigation_simulator.cc b/content/public/test/navigation_simulator.cc
index fa2bc91f..8a7db8a 100644
--- a/content/public/test/navigation_simulator.cc
+++ b/content/public/test/navigation_simulator.cc
@@ -556,13 +556,18 @@
 void NavigationSimulator::WaitForThrottleChecksComplete() {
   // If last_throttle_check_result_ is set, then throttle checks completed
   // synchronously.
-  if (last_throttle_check_result_)
-    return;
+  if (!last_throttle_check_result_) {
+    base::RunLoop run_loop;
+    throttle_checks_wait_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    throttle_checks_wait_closure_.Reset();
+  }
 
-  base::RunLoop run_loop;
-  throttle_checks_wait_closure_ = run_loop.QuitClosure();
-  run_loop.Run();
-  throttle_checks_wait_closure_.Reset();
+  if (IsBrowserSideNavigationEnabled()) {
+    // Run message loop once since NavigationRequest::OnStartChecksComplete
+    // posted a task.
+    base::RunLoop().RunUntilIdle();
+  }
 }
 
 void NavigationSimulator::OnThrottleChecksComplete(
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 3c12a32..46e340a 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -189,6 +189,8 @@
 
 #if defined(OS_MACOSX)
 #include "base/mac/mac_util.h"
+#include "base/process/process.h"
+#include "content/common/mac/app_nap_activity.h"
 #include "content/renderer/theme_helper_mac.h"
 #include "content/renderer/webscrollbarbehavior_impl_mac.h"
 #endif
@@ -809,6 +811,10 @@
   Boolean value = CFPreferencesGetAppBooleanValue(
       key, kCFPreferencesCurrentApplication, &key_exists);
   is_elastic_overscroll_enabled_ = !key_exists || value;
+
+  if (base::Process::IsAppNapEnabled()) {
+    AppNapActivity::InitializeAppNapSupport();
+  }
 #else
   is_elastic_overscroll_enabled_ = false;
 #endif
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index fc29dfc3..b2be86f 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -559,6 +559,7 @@
         'WEBGL_debug_shaders',
         'WEBGL_depth_texture',
         'WEBKIT_WEBGL_depth_texture',
+        'WEBGL_draw_buffers',
         'WEBGL_lose_context',
         'WEBKIT_WEBGL_lose_context',
       ]
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index a7b4c5c..ce36682 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -109,34 +109,14 @@
         bug=1966) # angle bug ID
 
     # Passthrough command decoder
-    self.Fail('conformance/extensions/ext-sRGB.html',
-        ['passthrough'], bug=679696)
     self.Fail('conformance/extensions/webgl-draw-buffers.html',
         ['passthrough'], bug=1523) # angle bug ID
     self.Fail('conformance/glsl/misc/shaders-with-name-conflicts.html',
         ['passthrough'], bug=1639) # angle bug ID
-    self.Fail('conformance/misc/invalid-passed-params.html',
-        ['passthrough'], bug=1639) # angle bug ID
     self.Fail('conformance/misc/uninitialized-test.html',
         ['passthrough'], bug=1635) # angle bug ID
     self.Fail('conformance/reading/read-pixels-test.html',
         ['passthrough'], bug=1639) # angle bug ID
-    self.Fail('conformance/renderbuffers/renderbuffer-initialization.html',
-        ['passthrough'], bug=1635) # angle bug ID
-    self.Fail('conformance/textures/misc/texture-mips.html',
-        ['passthrough'], bug=665518)
-    self.Skip('conformance/textures/canvas/*',
-        ['passthrough'], bug=1932) # angle bug ID
-    self.Skip('conformance/textures/video/*',
-        ['passthrough'], bug=1932) # angle bug ID
-    self.Skip('conformance/textures/image_bitmap_from_canvas/*',
-        ['passthrough'], bug=1932) # angle bug ID
-    self.Skip('conformance/textures/webgl_canvas/*',
-        ['passthrough'], bug=1932) # angle bug ID
-    self.Skip('conformance/extensions/oes-texture-float-with-canvas.html',
-        ['passthrough'], bug=1932) # angle bug ID
-    self.Skip('conformance/extensions/oes-texture-float-with-video.html',
-        ['passthrough'], bug=1932) # angle bug ID
 
     # Passthrough command decoder / OpenGL
     self.Fail('conformance/buffers/buffer-uninitialized.html',
@@ -148,6 +128,12 @@
         ['passthrough', 'opengl'], bug=665521)
     self.Fail('conformance/renderbuffers/framebuffer-test.html',
         ['passthrough', 'opengl'], bug=665521)
+    self.Fail('conformance/textures/canvas/*',
+        ['passthrough', 'opengl'], bug=1932) # angle bug ID
+    self.Fail('conformance/textures/image_bitmap_from_canvas/*',
+        ['passthrough', 'opengl'], bug=1932) # angle bug ID
+    self.Fail('conformance/textures/webgl_canvas/*',
+        ['passthrough', 'opengl'], bug=1932) # angle bug ID
     self.Fail('conformance/textures/misc/copy-tex-image-and-sub-image-2d.html',
         ['passthrough', 'opengl'], bug=665521)
     self.Fail('conformance/textures/misc/copytexsubimage2d-subrects.html',
@@ -158,6 +144,10 @@
         ['passthrough', 'opengl'], bug=665521)
     self.Fail('conformance/textures/misc/texture-fakeblack.html',
         ['passthrough', 'opengl'], bug=665521)
+    self.Fail('conformance/textures/misc/texture-mips.html',
+        ['passthrough', 'opengl'], bug=665518)
+    self.Fail('conformance/extensions/oes-texture-float-with-canvas.html',
+        ['passthrough', 'opengl'], bug=1932) # angle bug ID
 
     # Passthrough command decoder / OpenGL / Intel
     self.Fail('conformance/renderbuffers/framebuffer-object-attachment.html',
@@ -192,12 +182,18 @@
     # Passthrough command decoder / D3D11
     self.Fail('conformance/glsl/misc/shaders-with-uniform-structs.html',
         ['passthrough', 'd3d11'], bug=1639) # angle bug ID
-    self.Fail('conformance/renderbuffers/framebuffer-object-attachment.html',
-        ['passthrough', 'd3d11'], bug=602688)
     self.Fail('conformance/textures/misc/copy-tex-image-and-sub-image-2d.html',
         ['passthrough', 'd3d11'], bug=1639) # angle bug ID
-    self.Fail('conformance/textures/misc/texture-attachment-formats.html',
-        ['passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/textures/canvas/' +
+        'tex-2d-rgb-rgb-unsigned_short_5_6_5.html',
+        ['passthrough', 'd3d11'], bug=1635) # angle bug ID
+    self.Fail('conformance/textures/canvas/' +
+        'tex-2d-rgb-rgb-unsigned_byte.html',
+        ['passthrough', 'd3d11'], bug=1635) # angle bug ID
+    self.Fail('conformance/textures/misc/gl-teximage.html',
+        ['passthrough', 'd3d11'], bug=1635) # angle bug ID
+    self.Fail('conformance/textures/misc/texture-mips.html',
+        ['passthrough', 'd3d11'], bug=1635) # angle bug ID
 
     # Win / AMD / Passthrough command decoder / D3D11
     self.Flaky('conformance/textures/misc/copytexsubimage2d-subrects.html',
diff --git a/crypto/apple_keychain.h b/crypto/apple_keychain.h
index 1037b7e..ca681df 100644
--- a/crypto/apple_keychain.h
+++ b/crypto/apple_keychain.h
@@ -54,50 +54,7 @@
                                       SecKeychainItemRef* itemRef) const;
 
 #if !defined(OS_IOS)
-  virtual OSStatus ItemCopyAttributesAndData(
-      SecKeychainItemRef itemRef,
-      SecKeychainAttributeInfo* info,
-      SecItemClass* itemClass,
-      SecKeychainAttributeList** attrList,
-      UInt32* length,
-      void** outData) const;
-
-  virtual OSStatus ItemModifyAttributesAndData(
-      SecKeychainItemRef itemRef,
-      const SecKeychainAttributeList* attrList,
-      UInt32 length,
-      const void* data) const;
-
-  virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList,
-                                             void* data) const;
-
   virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const;
-
-  virtual OSStatus SearchCreateFromAttributes(
-      CFTypeRef keychainOrArray,
-      SecItemClass itemClass,
-      const SecKeychainAttributeList* attrList,
-      SecKeychainSearchRef* searchRef) const;
-
-  virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
-                                  SecKeychainItemRef* itemRef) const;
-
-  virtual OSStatus AddInternetPassword(SecKeychainRef keychain,
-                                       UInt32 serverNameLength,
-                                       const char* serverName,
-                                       UInt32 securityDomainLength,
-                                       const char* securityDomain,
-                                       UInt32 accountNameLength,
-                                       const char* accountName,
-                                       UInt32 pathLength, const char* path,
-                                       UInt16 port, SecProtocolType protocol,
-                                       SecAuthenticationType authenticationType,
-                                       UInt32 passwordLength,
-                                       const void* passwordData,
-                                       SecKeychainItemRef* itemRef) const;
-
-  // Calls CFRelease on the given ref, after checking that |ref| is non-NULL.
-  virtual void Free(CFTypeRef ref) const;
 #endif  // !defined(OS_IOS)
 
  private:
diff --git a/crypto/apple_keychain_mac.mm b/crypto/apple_keychain_mac.mm
index ff5fa3a..a3620485 100644
--- a/crypto/apple_keychain_mac.mm
+++ b/crypto/apple_keychain_mac.mm
@@ -15,93 +15,11 @@
 
 AppleKeychain::~AppleKeychain() {}
 
-OSStatus AppleKeychain::ItemCopyAttributesAndData(
-    SecKeychainItemRef itemRef,
-    SecKeychainAttributeInfo* info,
-    SecItemClass* itemClass,
-    SecKeychainAttributeList** attrList,
-    UInt32* length,
-    void** outData) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-  return SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass,
-                                              attrList, length, outData);
-}
-
-OSStatus AppleKeychain::ItemModifyAttributesAndData(
-    SecKeychainItemRef itemRef,
-    const SecKeychainAttributeList* attrList,
-    UInt32 length,
-    const void* data) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-  return SecKeychainItemModifyAttributesAndData(itemRef, attrList, length,
-                                                data);
-}
-
-OSStatus AppleKeychain::ItemFreeAttributesAndData(
-    SecKeychainAttributeList* attrList,
-    void* data) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-  return SecKeychainItemFreeAttributesAndData(attrList, data);
-}
-
 OSStatus AppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
   base::AutoLock lock(GetMacSecurityServicesLock());
   return SecKeychainItemDelete(itemRef);
 }
 
-OSStatus AppleKeychain::SearchCreateFromAttributes(
-    CFTypeRef keychainOrArray,
-    SecItemClass itemClass,
-    const SecKeychainAttributeList* attrList,
-    SecKeychainSearchRef* searchRef) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-// Eventually, this deprecated method should be removed entirely.
-// https://crbug.com/595468
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-  return SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass,
-                                               attrList, searchRef);
-#pragma clang diagnostic pop
-}
-
-OSStatus AppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
-                                       SecKeychainItemRef* itemRef) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-// Eventually, this deprecated method should be removed entirely.
-// https://crbug.com/595468
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-  return SecKeychainSearchCopyNext(searchRef, itemRef);
-#pragma clang diagnostic pop
-}
-
-OSStatus AppleKeychain::AddInternetPassword(
-    SecKeychainRef keychain,
-    UInt32 serverNameLength,
-    const char* serverName,
-    UInt32 securityDomainLength,
-    const char* securityDomain,
-    UInt32 accountNameLength,
-    const char* accountName,
-    UInt32 pathLength,
-    const char* path,
-    UInt16 port,
-    SecProtocolType protocol,
-    SecAuthenticationType authenticationType,
-    UInt32 passwordLength,
-    const void* passwordData,
-    SecKeychainItemRef* itemRef) const {
-  base::AutoLock lock(GetMacSecurityServicesLock());
-  return SecKeychainAddInternetPassword(keychain,
-                                        serverNameLength, serverName,
-                                        securityDomainLength, securityDomain,
-                                        accountNameLength, accountName,
-                                        pathLength, path,
-                                        port, protocol, authenticationType,
-                                        passwordLength, passwordData,
-                                        itemRef);
-}
-
 OSStatus AppleKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
                                             UInt32 serviceNameLength,
                                             const char* serviceName,
@@ -146,9 +64,4 @@
                                        itemRef);
 }
 
-void AppleKeychain::Free(CFTypeRef ref) const {
-  if (ref)
-    CFRelease(ref);
-}
-
 }  // namespace crypto
diff --git a/crypto/mock_apple_keychain.cc b/crypto/mock_apple_keychain.cc
index f73ba24..173cfa7 100644
--- a/crypto/mock_apple_keychain.cc
+++ b/crypto/mock_apple_keychain.cc
@@ -72,9 +72,6 @@
 
   DCHECK_GT(passwordLength, 0U);
   DCHECK(passwordData);
-  add_generic_password_ =
-      std::string(const_cast<char*>(static_cast<const char*>(passwordData)),
-                  passwordLength);
   return noErr;
 }
 
diff --git a/crypto/mock_apple_keychain.h b/crypto/mock_apple_keychain.h
index 38e69f2..b256a22 100644
--- a/crypto/mock_apple_keychain.h
+++ b/crypto/mock_apple_keychain.h
@@ -19,9 +19,7 @@
 namespace crypto {
 
 // Mock Keychain wrapper for testing code that interacts with the OS X
-// Keychain.  Implemented by storing SecKeychainAttributeList and
-// KeychainPasswordData values in separate mutable containers and
-// mapping them to integer keys.
+// Keychain.
 //
 // Note that "const" is pretty much meaningless for this class; the const-ness
 // of AppleKeychain doesn't apply to the actual keychain data, so all of the
@@ -56,71 +54,7 @@
   std::string GetEncryptionPassword() const;
 
 #if !defined(OS_IOS)
-  OSStatus ItemCopyAttributesAndData(SecKeychainItemRef itemRef,
-                                     SecKeychainAttributeInfo* info,
-                                     SecItemClass* itemClass,
-                                     SecKeychainAttributeList** attrList,
-                                     UInt32* length,
-                                     void** outData) const override;
-  // Pass "fail_me" as the data to get errSecAuthFailed.
-  OSStatus ItemModifyAttributesAndData(SecKeychainItemRef itemRef,
-                                       const SecKeychainAttributeList* attrList,
-                                       UInt32 length,
-                                       const void* data) const override;
-  OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList,
-                                     void* data) const override;
   OSStatus ItemDelete(SecKeychainItemRef itemRef) const override;
-  OSStatus SearchCreateFromAttributes(
-      CFTypeRef keychainOrArray,
-      SecItemClass itemClass,
-      const SecKeychainAttributeList* attrList,
-      SecKeychainSearchRef* searchRef) const override;
-  OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
-                          SecKeychainItemRef* itemRef) const override;
-  // Pass "some.domain.com" as the serverName to get errSecDuplicateItem.
-  OSStatus AddInternetPassword(SecKeychainRef keychain,
-                               UInt32 serverNameLength,
-                               const char* serverName,
-                               UInt32 securityDomainLength,
-                               const char* securityDomain,
-                               UInt32 accountNameLength,
-                               const char* accountName,
-                               UInt32 pathLength,
-                               const char* path,
-                               UInt16 port,
-                               SecProtocolType protocol,
-                               SecAuthenticationType authenticationType,
-                               UInt32 passwordLength,
-                               const void* passwordData,
-                               SecKeychainItemRef* itemRef) const override;
-  void Free(CFTypeRef ref) const override;
-
-  // Return the counts of objects returned by Create/Copy functions but never
-  // Free'd as they should have been.
-  int UnfreedSearchCount() const;
-  int UnfreedKeychainItemCount() const;
-  int UnfreedAttributeDataCount() const;
-
-  // Returns true if all items added with AddInternetPassword have a creator
-  // code set.
-  bool CreatorCodesSetForAddedItems() const;
-
-  struct KeychainTestData {
-    const SecAuthenticationType auth_type;
-    const char* server;
-    const SecProtocolType protocol;
-    const char* path;
-    const UInt32 port;
-    const char* security_domain;
-    const char* creation_date;
-    const char* username;
-    const char* password;
-    const bool negative_item;
-  };
-  // Adds a keychain item with the given info to the test set.
-  void AddTestItem(const KeychainTestData& item_data);
-
-  void set_locked(bool locked) { locked_ = locked; }
 #endif  // !defined(OS_IOS)
 
   // |FindGenericPassword()| can return different results depending on user
@@ -135,109 +69,10 @@
   // Returns the true if |AddGenericPassword()| was called.
   bool called_add_generic() const { return called_add_generic_; }
 
-  // Returns the value of the password set when |AddGenericPassword()| was
-  // called.
-  std::string add_generic_password() const { return add_generic_password_; }
-
   // Returns the number of allocations - deallocations for password data.
   int password_data_count() const { return password_data_count_; }
 
  private:
-  // Type used for the keys in the std::map(s) and MockAppleKeychain items.
-  typedef uintptr_t MockKeychainItemType;
-
-  // Type of the map holding the mock keychain attributes.
-  typedef std::map<MockKeychainItemType, SecKeychainAttributeList>
-      MockKeychainAttributesMap;
-
-#if !defined(OS_IOS)
-  // Returns true if the keychain already contains a password that matches the
-  // attributes provided.
-  bool AlreadyContainsInternetPassword(
-      UInt32 serverNameLength,
-      const char* serverName,
-      UInt32 securityDomainLength,
-      const char* securityDomain,
-      UInt32 accountNameLength,
-      const char* accountName,
-      UInt32 pathLength,
-      const char* path,
-      UInt16 port,
-      SecProtocolType protocol,
-      SecAuthenticationType authenticationType) const;
-  // Initializes storage for keychain data at |key|.
-  void InitializeKeychainData(MockKeychainItemType key) const;
-  // Sets the data and length of |tag| in the item-th test item.
-  void SetTestDataBytes(
-      MockKeychainItemType item,
-      UInt32 tag,
-      const void* data,
-      size_t length);
-  // Sets the data and length of |tag| in the item-th test item based on
-  // |value|. The null-terminator will not be included; the Keychain Services
-  // docs don't indicate whether it is or not, so clients should not assume
-  // that it will be.
-  void SetTestDataString(MockKeychainItemType item,
-                         UInt32 tag,
-                         const char* value);
-  // Sets the data of the corresponding attribute of the item-th test item to
-  // |value|. Assumes that the space has alread been allocated, and the length
-  // set.
-  void SetTestDataPort(MockKeychainItemType item, UInt32 value);
-  void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value);
-  void SetTestDataAuthType(MockKeychainItemType item,
-                           SecAuthenticationType value);
-  void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value);
-  void SetTestDataCreator(MockKeychainItemType item, OSType value);
-  // Sets the password data and length for the item-th test item.
-  void SetTestDataPasswordBytes(MockKeychainItemType item,
-                                const void* data,
-                                size_t length);
-  // Sets the password for the item-th test item. As with SetTestDataString,
-  // the data will not be null-terminated.
-  void SetTestDataPasswordString(MockKeychainItemType item, const char* value);
-
-  // Returns the address of the attribute in attribute_list with tag |tag|.
-  static SecKeychainAttribute* AttributeWithTag(
-      const SecKeychainAttributeList& attribute_list,
-      UInt32 tag);
-
-  static const SecKeychainSearchRef kDummySearchRef;
-
-  // Simulates the state when the user refuses to unclock the Keychain.
-  // If true, reading and modifying a password value result in errSecAuthFailed.
-  bool locked_;
-
-  typedef struct KeychainPasswordData {
-    KeychainPasswordData() : data(nullptr), length(0) {}
-    void* data;
-    UInt32 length;
-  } KeychainPasswordData;
-
-  // Mutable because the MockAppleKeychain API requires its internal keychain
-  // storage to be modifiable by users of this class.
-  mutable MockKeychainAttributesMap keychain_attr_list_;
-  mutable std::map<MockKeychainItemType,
-                   KeychainPasswordData> keychain_data_;
-  mutable MockKeychainItemType next_item_key_;
-
-  // Tracks the items that should be returned in subsequent calls to
-  // SearchCopyNext, based on the last call to SearchCreateFromAttributes.
-  // We can't handle multiple active searches, since we don't track the search
-  // ref we return, but we don't need to for our mocking.
-  mutable std::vector<MockKeychainItemType> remaining_search_results_;
-
-  // Track copies and releases to make sure they balance. Really these should
-  // be maps to track per item, but this should be good enough to catch
-  // real mistakes.
-  mutable int search_copy_count_;
-  mutable int keychain_item_copy_count_;
-  mutable int attribute_data_copy_count_;
-
-  // Tracks which items (by key) were added with AddInternetPassword.
-  mutable std::set<MockKeychainItemType> added_via_api_;
-#endif  // !defined(OS_IOS)
-
   // Result code for the |FindGenericPassword()| method.
   OSStatus find_generic_result_;
 
@@ -248,8 +83,7 @@
   // and |ItemFreeContent|.
   mutable int password_data_count_;
 
-  // Records the password being set when |AddGenericPassword()| gets called.
-  mutable std::string add_generic_password_;
+  DISALLOW_COPY_AND_ASSIGN(MockAppleKeychain);
 };
 
 }  // namespace crypto
diff --git a/crypto/mock_apple_keychain_mac.cc b/crypto/mock_apple_keychain_mac.cc
index e4bc30c..43a3410b3 100644
--- a/crypto/mock_apple_keychain_mac.cc
+++ b/crypto/mock_apple_keychain_mac.cc
@@ -11,512 +11,15 @@
 
 namespace crypto {
 
-// static
-const SecKeychainSearchRef MockAppleKeychain::kDummySearchRef =
-    reinterpret_cast<SecKeychainSearchRef>(1000);
-
 MockAppleKeychain::MockAppleKeychain()
-    : locked_(false),
-      next_item_key_(0),
-      search_copy_count_(0),
-      keychain_item_copy_count_(0),
-      attribute_data_copy_count_(0),
-      find_generic_result_(noErr),
+    : find_generic_result_(noErr),
       called_add_generic_(false),
       password_data_count_(0) {}
 
-void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key) const {
-  UInt32 tags[] = { kSecAccountItemAttr,
-                    kSecServerItemAttr,
-                    kSecPortItemAttr,
-                    kSecPathItemAttr,
-                    kSecProtocolItemAttr,
-                    kSecAuthenticationTypeItemAttr,
-                    kSecSecurityDomainItemAttr,
-                    kSecCreationDateItemAttr,
-                    kSecNegativeItemAttr,
-                    kSecCreatorItemAttr };
-  keychain_attr_list_[key] = SecKeychainAttributeList();
-  keychain_data_[key] = KeychainPasswordData();
-  keychain_attr_list_[key].count = arraysize(tags);
-  keychain_attr_list_[key].attr = static_cast<SecKeychainAttribute*>(
-      calloc(keychain_attr_list_[key].count, sizeof(SecKeychainAttribute)));
-  for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
-    keychain_attr_list_[key].attr[i].tag = tags[i];
-    size_t data_size = 0;
-    switch (tags[i]) {
-      case kSecPortItemAttr:
-        data_size = sizeof(UInt32);
-        break;
-      case kSecProtocolItemAttr:
-        data_size = sizeof(SecProtocolType);
-        break;
-      case kSecAuthenticationTypeItemAttr:
-        data_size = sizeof(SecAuthenticationType);
-        break;
-      case kSecNegativeItemAttr:
-        data_size = sizeof(Boolean);
-        break;
-      case kSecCreatorItemAttr:
-        data_size = sizeof(OSType);
-        break;
-    }
-    if (data_size > 0) {
-      keychain_attr_list_[key].attr[i].length = data_size;
-      keychain_attr_list_[key].attr[i].data = calloc(1, data_size);
-    }
-  }
-}
-
-MockAppleKeychain::~MockAppleKeychain() {
-  for (MockKeychainAttributesMap::iterator it = keychain_attr_list_.begin();
-       it != keychain_attr_list_.end();
-       ++it) {
-    for (unsigned int i = 0; i < it->second.count; ++i) {
-      if (it->second.attr[i].data)
-        free(it->second.attr[i].data);
-    }
-    free(it->second.attr);
-    if (keychain_data_[it->first].data)
-      free(keychain_data_[it->first].data);
-  }
-  keychain_attr_list_.clear();
-  keychain_data_.clear();
-}
-
-SecKeychainAttribute* MockAppleKeychain::AttributeWithTag(
-    const SecKeychainAttributeList& attribute_list,
-    UInt32 tag) {
-  int attribute_index = -1;
-  for (unsigned int i = 0; i < attribute_list.count; ++i) {
-    if (attribute_list.attr[i].tag == tag) {
-      attribute_index = i;
-      break;
-    }
-  }
-  if (attribute_index == -1) {
-    NOTREACHED() << "Unsupported attribute: " << tag;
-    return NULL;
-  }
-  return &(attribute_list.attr[attribute_index]);
-}
-
-void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item,
-                                         UInt32 tag,
-                                         const void* data,
-                                         size_t length) {
-  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
-                                                     tag);
-  attribute->length = length;
-  if (length > 0) {
-    if (attribute->data)
-      free(attribute->data);
-    attribute->data = malloc(length);
-    CHECK(attribute->data);
-    memcpy(attribute->data, data, length);
-  } else {
-    attribute->data = NULL;
-  }
-}
-
-void MockAppleKeychain::SetTestDataString(MockKeychainItemType item,
-                                          UInt32 tag,
-                                          const char* value) {
-  SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
-}
-
-void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item,
-                                        UInt32 value) {
-  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
-                                                     kSecPortItemAttr);
-  UInt32* data = static_cast<UInt32*>(attribute->data);
-  *data = value;
-}
-
-void MockAppleKeychain::SetTestDataProtocol(MockKeychainItemType item,
-                                            SecProtocolType value) {
-  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
-                                                     kSecProtocolItemAttr);
-  SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
-  *data = value;
-}
-
-void MockAppleKeychain::SetTestDataAuthType(MockKeychainItemType item,
-                                            SecAuthenticationType value) {
-  SecKeychainAttribute* attribute = AttributeWithTag(
-      keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
-  SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
-      attribute->data);
-  *data = value;
-}
-
-void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item,
-                                                Boolean value) {
-  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
-                                                     kSecNegativeItemAttr);
-  Boolean* data = static_cast<Boolean*>(attribute->data);
-  *data = value;
-}
-
-void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item,
-                                           OSType value) {
-  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
-                                                     kSecCreatorItemAttr);
-  OSType* data = static_cast<OSType*>(attribute->data);
-  *data = value;
-}
-
-void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item,
-                                                 const void* data,
-                                                 size_t length) {
-  keychain_data_[item].length = length;
-  if (length > 0) {
-    if (keychain_data_[item].data)
-      free(keychain_data_[item].data);
-    keychain_data_[item].data = malloc(length);
-    memcpy(keychain_data_[item].data, data, length);
-  } else {
-    keychain_data_[item].data = NULL;
-  }
-}
-
-void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item,
-                                                  const char* value) {
-  SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
-}
-
-OSStatus MockAppleKeychain::ItemCopyAttributesAndData(
-    SecKeychainItemRef itemRef,
-    SecKeychainAttributeInfo* info,
-    SecItemClass* itemClass,
-    SecKeychainAttributeList** attrList,
-    UInt32* length,
-    void** outData) const {
-  DCHECK(itemRef);
-  MockKeychainItemType key =
-      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
-  if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
-    return errSecInvalidItemRef;
-
-  DCHECK(!itemClass);  // itemClass not implemented in the Mock.
-  if (locked_ && outData)
-    return errSecAuthFailed;
-
-  if (attrList)
-    *attrList  = &(keychain_attr_list_[key]);
-  if (outData) {
-    *outData = keychain_data_[key].data;
-    DCHECK(length);
-    *length = keychain_data_[key].length;
-  }
-
-  ++attribute_data_copy_count_;
-  return noErr;
-}
-
-OSStatus MockAppleKeychain::ItemModifyAttributesAndData(
-    SecKeychainItemRef itemRef,
-    const SecKeychainAttributeList* attrList,
-    UInt32 length,
-    const void* data) const {
-  DCHECK(itemRef);
-  if (locked_)
-    return errSecAuthFailed;
-  const char* fail_trigger = "fail_me";
-  if (length == strlen(fail_trigger) &&
-      memcmp(data, fail_trigger, length) == 0) {
-    return errSecAuthFailed;
-  }
-
-  MockKeychainItemType key =
-      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
-  if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
-    return errSecInvalidItemRef;
-
-  MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
-  if (attrList) {
-    for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
-      if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
-        void* data = attrList->attr[change_attr].data;
-        mutable_this->SetTestDataCreator(key, *(static_cast<OSType*>(data)));
-      } else {
-        NOTIMPLEMENTED();
-      }
-    }
-  }
-  if (data)
-    mutable_this->SetTestDataPasswordBytes(key, data, length);
-  return noErr;
-}
-
-OSStatus MockAppleKeychain::ItemFreeAttributesAndData(
-    SecKeychainAttributeList* attrList,
-    void* data) const {
-  --attribute_data_copy_count_;
-  return noErr;
-}
+MockAppleKeychain::~MockAppleKeychain() {}
 
 OSStatus MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
-  if (locked_)
-    return errSecAuthFailed;
-  MockKeychainItemType key =
-      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
-
-  for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
-    if (keychain_attr_list_[key].attr[i].data)
-      free(keychain_attr_list_[key].attr[i].data);
-  }
-  free(keychain_attr_list_[key].attr);
-  if (keychain_data_[key].data)
-    free(keychain_data_[key].data);
-
-  keychain_attr_list_.erase(key);
-  keychain_data_.erase(key);
-  added_via_api_.erase(key);
   return noErr;
 }
 
-OSStatus MockAppleKeychain::SearchCreateFromAttributes(
-    CFTypeRef keychainOrArray,
-    SecItemClass itemClass,
-    const SecKeychainAttributeList* attrList,
-    SecKeychainSearchRef* searchRef) const {
-  // Figure out which of our mock items matches, and set up the array we'll use
-  // to generate results out of SearchCopyNext.
-  remaining_search_results_.clear();
-  for (MockKeychainAttributesMap::const_iterator it =
-           keychain_attr_list_.begin();
-       it != keychain_attr_list_.end();
-       ++it) {
-    bool mock_item_matches = true;
-    for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
-      SecKeychainAttribute* mock_attribute =
-          AttributeWithTag(it->second, attrList->attr[search_attr].tag);
-      if (mock_attribute->length != attrList->attr[search_attr].length ||
-          memcmp(mock_attribute->data, attrList->attr[search_attr].data,
-                 attrList->attr[search_attr].length) != 0) {
-        mock_item_matches = false;
-        break;
-      }
-    }
-    if (mock_item_matches)
-      remaining_search_results_.push_back(it->first);
-  }
-
-  DCHECK(searchRef);
-  *searchRef = kDummySearchRef;
-  ++search_copy_count_;
-  return noErr;
-}
-
-bool MockAppleKeychain::AlreadyContainsInternetPassword(
-    UInt32 serverNameLength,
-    const char* serverName,
-    UInt32 securityDomainLength,
-    const char* securityDomain,
-    UInt32 accountNameLength,
-    const char* accountName,
-    UInt32 pathLength,
-    const char* path,
-    UInt16 port,
-    SecProtocolType protocol,
-    SecAuthenticationType authenticationType) const {
-  for (MockKeychainAttributesMap::const_iterator it =
-           keychain_attr_list_.begin();
-       it != keychain_attr_list_.end();
-       ++it) {
-    SecKeychainAttribute* attribute;
-    attribute = AttributeWithTag(it->second, kSecServerItemAttr);
-    if ((attribute->length != serverNameLength) ||
-        (attribute->data == NULL && *serverName != '\0') ||
-        (attribute->data != NULL && *serverName == '\0') ||
-        strncmp(serverName,
-                (const char*) attribute->data,
-                serverNameLength) != 0) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecSecurityDomainItemAttr);
-    if ((attribute->length != securityDomainLength) ||
-        (attribute->data == NULL && *securityDomain != '\0') ||
-        (attribute->data != NULL && *securityDomain == '\0') ||
-        strncmp(securityDomain,
-                (const char*) attribute->data,
-                securityDomainLength) != 0) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecAccountItemAttr);
-    if ((attribute->length != accountNameLength) ||
-        (attribute->data == NULL && *accountName != '\0') ||
-        (attribute->data != NULL && *accountName == '\0') ||
-        strncmp(accountName,
-                (const char*) attribute->data,
-                accountNameLength) != 0) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecPathItemAttr);
-    if ((attribute->length != pathLength) ||
-        (attribute->data == NULL && *path != '\0') ||
-        (attribute->data != NULL && *path == '\0') ||
-        strncmp(path,
-                (const char*) attribute->data,
-                pathLength) != 0) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecPortItemAttr);
-    if ((attribute->data == NULL) ||
-        (port != *(static_cast<UInt32*>(attribute->data)))) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecProtocolItemAttr);
-    if ((attribute->data == NULL) ||
-        (protocol != *(static_cast<SecProtocolType*>(attribute->data)))) {
-      continue;
-    }
-    attribute = AttributeWithTag(it->second, kSecAuthenticationTypeItemAttr);
-    if ((attribute->data == NULL) ||
-        (authenticationType !=
-            *(static_cast<SecAuthenticationType*>(attribute->data)))) {
-      continue;
-    }
-    // The keychain already has this item, since all fields other than the
-    // password match.
-    return true;
-  }
-  return false;
-}
-
-OSStatus MockAppleKeychain::AddInternetPassword(
-    SecKeychainRef keychain,
-    UInt32 serverNameLength,
-    const char* serverName,
-    UInt32 securityDomainLength,
-    const char* securityDomain,
-    UInt32 accountNameLength,
-    const char* accountName,
-    UInt32 pathLength,
-    const char* path,
-    UInt16 port,
-    SecProtocolType protocol,
-    SecAuthenticationType authenticationType,
-    UInt32 passwordLength,
-    const void* passwordData,
-    SecKeychainItemRef* itemRef) const {
-  if (locked_)
-    return errSecAuthFailed;
-
-  // Check for the magic duplicate item trigger.
-  if (strcmp(serverName, "some.domain.com") == 0)
-    return errSecDuplicateItem;
-
-  // If the account already exists in the keychain, we don't add it.
-  if (AlreadyContainsInternetPassword(serverNameLength, serverName,
-                                      securityDomainLength, securityDomain,
-                                      accountNameLength, accountName,
-                                      pathLength, path,
-                                      port, protocol,
-                                      authenticationType)) {
-    return errSecDuplicateItem;
-  }
-
-  // Pick the next unused slot.
-  MockKeychainItemType key = next_item_key_++;
-
-  // Initialize keychain data storage at the target location.
-  InitializeKeychainData(key);
-
-  MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
-  mutable_this->SetTestDataBytes(key, kSecServerItemAttr, serverName,
-                                 serverNameLength);
-  mutable_this->SetTestDataBytes(key, kSecSecurityDomainItemAttr,
-                                 securityDomain, securityDomainLength);
-  mutable_this->SetTestDataBytes(key, kSecAccountItemAttr, accountName,
-                                 accountNameLength);
-  mutable_this->SetTestDataBytes(key, kSecPathItemAttr, path, pathLength);
-  mutable_this->SetTestDataPort(key, port);
-  mutable_this->SetTestDataProtocol(key, protocol);
-  mutable_this->SetTestDataAuthType(key, authenticationType);
-  mutable_this->SetTestDataPasswordBytes(key, passwordData,
-                                         passwordLength);
-  base::Time::Exploded exploded_time;
-  base::Time::Now().UTCExplode(&exploded_time);
-  char time_string[128];
-  snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
-           exploded_time.year, exploded_time.month, exploded_time.day_of_month,
-           exploded_time.hour, exploded_time.minute, exploded_time.second);
-  mutable_this->SetTestDataString(key, kSecCreationDateItemAttr, time_string);
-
-  added_via_api_.insert(key);
-
-  if (itemRef) {
-    *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
-    ++keychain_item_copy_count_;
-  }
-  return noErr;
-}
-
-OSStatus MockAppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
-                                           SecKeychainItemRef* itemRef) const {
-  if (remaining_search_results_.empty())
-    return errSecItemNotFound;
-  MockKeychainItemType key = remaining_search_results_.front();
-  remaining_search_results_.erase(remaining_search_results_.begin());
-  *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
-  ++keychain_item_copy_count_;
-  return noErr;
-}
-
-void MockAppleKeychain::Free(CFTypeRef ref) const {
-  if (!ref)
-    return;
-
-  if (ref == kDummySearchRef) {
-    --search_copy_count_;
-  } else {
-    --keychain_item_copy_count_;
-  }
-}
-
-int MockAppleKeychain::UnfreedSearchCount() const {
-  return search_copy_count_;
-}
-
-int MockAppleKeychain::UnfreedKeychainItemCount() const {
-  return keychain_item_copy_count_;
-}
-
-int MockAppleKeychain::UnfreedAttributeDataCount() const {
-  return attribute_data_copy_count_;
-}
-
-bool MockAppleKeychain::CreatorCodesSetForAddedItems() const {
-  for (std::set<MockKeychainItemType>::const_iterator
-           i = added_via_api_.begin();
-       i != added_via_api_.end();
-       ++i) {
-    SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
-                                                       kSecCreatorItemAttr);
-    OSType* data = static_cast<OSType*>(attribute->data);
-    if (*data == 0)
-      return false;
-  }
-  return true;
-}
-
-void MockAppleKeychain::AddTestItem(const KeychainTestData& item_data) {
-  MockKeychainItemType key = next_item_key_++;
-
-  InitializeKeychainData(key);
-  SetTestDataAuthType(key, item_data.auth_type);
-  SetTestDataString(key, kSecServerItemAttr, item_data.server);
-  SetTestDataProtocol(key, item_data.protocol);
-  SetTestDataString(key, kSecPathItemAttr, item_data.path);
-  SetTestDataPort(key, item_data.port);
-  SetTestDataString(key, kSecSecurityDomainItemAttr,
-                    item_data.security_domain);
-  SetTestDataString(key, kSecCreationDateItemAttr, item_data.creation_date);
-  SetTestDataString(key, kSecAccountItemAttr, item_data.username);
-  SetTestDataPasswordString(key, item_data.password);
-  SetTestDataNegativeItem(key, item_data.negative_item);
-}
-
 }  // namespace crypto
diff --git a/crypto/nss_crypto_module_delegate.h b/crypto/nss_crypto_module_delegate.h
index cf08f28..05be77d 100644
--- a/crypto/nss_crypto_module_delegate.h
+++ b/crypto/nss_crypto_module_delegate.h
@@ -7,9 +7,6 @@
 
 #include <string>
 
-#include "base/callback_forward.h"
-#include "crypto/scoped_nss_types.h"
-
 namespace crypto {
 
 // PK11_SetPasswordFunc is a global setting.  An implementation of
@@ -37,16 +34,6 @@
                                       bool* cancelled) = 0;
 };
 
-// Extends CryptoModuleBlockingPasswordDelegate with the ability to return a
-// slot in which to act. (Eg, which slot to store a generated key in.)
-class NSSCryptoModuleDelegate : public CryptoModuleBlockingPasswordDelegate {
- public:
-  ~NSSCryptoModuleDelegate() override {}
-
-  // Get the slot to store the generated key.
-  virtual ScopedPK11Slot RequestSlot() = 0;
-};
-
 }  // namespace crypto
 
 #endif  // CRYPTO_NSS_CRYPTO_MODULE_DELEGATE_H_
diff --git a/device/vr/android/gvr/gvr_delegate.cc b/device/vr/android/gvr/gvr_delegate.cc
index ceb673c..0230822 100644
--- a/device/vr/android/gvr/gvr_delegate.cc
+++ b/device/vr/android/gvr/gvr_delegate.cc
@@ -102,10 +102,10 @@
     gfx::DecomposedTransform decomposed_transform;
     gfx::DecomposeTransform(&decomposed_transform, transform);
 
-    pose->orientation.value()[0] = decomposed_transform.quaternion[0];
-    pose->orientation.value()[1] = decomposed_transform.quaternion[1];
-    pose->orientation.value()[2] = decomposed_transform.quaternion[2];
-    pose->orientation.value()[3] = decomposed_transform.quaternion[3];
+    pose->orientation.value()[0] = decomposed_transform.quaternion.x();
+    pose->orientation.value()[1] = decomposed_transform.quaternion.y();
+    pose->orientation.value()[2] = decomposed_transform.quaternion.z();
+    pose->orientation.value()[3] = decomposed_transform.quaternion.w();
 
     pose->position.emplace(3);
     pose->position.value()[0] = decomposed_transform.translate[0];
diff --git a/docs/task_scheduler_migration.md b/docs/task_scheduler_migration.md
new file mode 100644
index 0000000..9861bde
--- /dev/null
+++ b/docs/task_scheduler_migration.md
@@ -0,0 +1,77 @@
+# TaskScheduler Migration
+
+[TOC]
+
+## Overview
+
+[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h)
+was introduced to Chrome in Q1. The API is fully documented under [Threading and
+Tasks in Chrome](threading_and_tasks.md). This page will go into more details
+about how to migrate callers of existing APIs to TaskScheduler.
+
+Much of the migration has already been automated but the callers that remain
+require manual intervention from the OWNERS.
+
+## BlockingPool (and other SequencedWorkerPools)
+
+The remaining callers of BrowserThread::GetBlockingPool() require manual
+intervention because they're plumbing the SequencedWorkerPool multiple layers
+into another component.
+
+The TaskScheduler API explicitly discourages this paradigm. Instead exposing a
+static API from post_task.h and encouraging that individual components grab the
+TaskRunner/TaskTraits they need instead of bring injected one from their owner
+and hoping for the right traits. This often allows cleaning up multiple layers
+of plumbing without otherwise hurting testing as documented
+[here](threading_and_tasks.md#TaskRunner-ownership-encourage-no-dependency-injection).
+
+## BrowserThreads
+
+All BrowserThreads but UI/IO are being migrated to TaskScheduler
+(i.e. FILE/FILE_USER_BLOCKING/DB/PROCESS_LAUNCHER/CACHE).
+
+This migration requires manual intervention because:
+ 1. Everything on BrowserThread::FOO has to be assumed to depend on being
+    sequenced with everything else on BrowserThread::FOO until decided otherwise
+    by a developer.
+ 2. Everything on BrowserThread::FOO has to be assumed to be thread-affine until
+    decided otherwise by a developer.
+
+As a developer your goal is to get rid of all uses of BrowserThread::FOO in your assigned files by:
+ 1. Splitting things into their own execution sequence (i.e. post to a TaskRunner
+    obtained from post_task.h -- see [Threading and Tasks in
+    Chrome](threading_and_tasks.md) for details).
+ 2. Ideally migrating from a single-threaded context to a [much preferred]
+    (threading_and_tasks.md#Prefer-Sequences-to-Threads) sequenced context.
+    * Note: if your tasks use COM APIs (Component Object Model on Windows),
+      you'll need to use CreateCOMSTATaskRunnerWithTraits() and sequencing will
+      not be an option.
+
+## Relevant single-thread -> sequence mappings
+
+* base::SingleThreadTaskRunner -> base::SequencedTaskRunner
+* base::ThreadTaskRunnerHandle -> base::SequencedTaskRunnerHandle
+* base::ThreadChecker -> base::SequenceChecker
+* BrowserThread::DeleteOnThread -> base::DeleteOnTaskRunner / base::RefCountedDeleteOnSequence
+* CreateSingleThreadTaskRunnerWithTraits() -> CreateSequencedTaskRunnerWithTraits()
+   * Every CreateSingleThreadTaskRunnerWithTraits() usage should be accompanied
+     with a comment and ideally a bug to make it sequence when the sequence-unfriendly
+     dependency is addressed (again [Prefer Sequences to
+     Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads)).
+
+### Other relevant mappings for tests
+
+* base::MessageLoop -> base::test::ScopedTaskEnvironment
+* content::TestBrowserThread -> content::TestBrowserThreadBundle (if you still
+  need other BrowserThreads and ScopedTaskEnvironment if you don't)
+* base::RunLoop().Run() -(maybe)> content::RunAllBlockingPoolTasksUntilIdle()
+   * If test code was previously using RunLoop to execute things off the main
+     thread (as TestBrowserThreadBundle grouped everything under a single
+     MessageLoop), flushing tasks will now require asking for that explicitly.
+   * Or ScopedTaskEnvironment::RunUntilIdle() if you're not using
+     TestBrowserThreadBundle.
+   * If you need to control the order of execution of main thread versus
+     scheduler you can individually RunLoop.Run() and
+     TaskScheduler::FlushForTesting()
+   * If you need the TaskScheduler to not run anything until explicitly asked to
+     use ScopedTaskEnvironment::ExecutionMode::QUEUED.
diff --git a/docs/threading_and_tasks.md b/docs/threading_and_tasks.md
index befa534..9b28921 100644
--- a/docs/threading_and_tasks.md
+++ b/docs/threading_and_tasks.md
@@ -50,7 +50,7 @@
 
 ### Prefer Sequences to Threads
 
-**Sequenced execution mode is far prefered to Single Threaded** in scenarios
+**Sequenced execution mode is far preferred to Single Threaded** in scenarios
 that require mere thread-safety as it opens up scheduling paradigms that
 wouldn't be possible otherwise (sequences can hop threads instead of being stuck
 behind unrelated work on a dedicated thread). Ability to hop threads also means
@@ -633,7 +633,7 @@
 // TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN may still be
 // running.
 ```
-## TaskRunner ownership
+## TaskRunner ownership (encourage no dependency injection)
 
 TaskRunners shouldn't be passed through several components. Instead, the
 components that uses a TaskRunner should be the one that creates it.
@@ -643,3 +643,24 @@
 used in an eventual leaf. The leaf can and should now obtain its TaskRunner
 directly from
 [`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h).
+
+Dependency injection of TaskRunners can still seldomly be useful to unit test a
+component when triggering a specific race in a specific way is essential to the
+test. For such cases the preferred approach is the following:
+
+```cpp
+class FooWithCustomizableTaskRunnerForTesting {
+ public:
+
+  void SetBackgroundTaskRunnerForTesting(
+      scoped_refptr<base::SequenceTaskRunner> background_task_runner);
+
+ private:
+  scoped_refptr<base::SequenceTaskRunner> background_task_runner_ =
+      base::CreateSequenceTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND});
+}
+```
+
+Note that this still allows removing all layers of plumbing between //chrome and
+that component since unit tests will use the leaf layer directly.
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_action.cc b/extensions/browser/api/declarative_webrequest/webrequest_action.cc
index 6c7589db..39f945b 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_action.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_action.cc
@@ -516,8 +516,8 @@
   // TODO(devlin): Pass in the real tab id here.
   return WebRequestPermissions::CanExtensionAccessURL(
              extension_info_map, extension_id, request->url(), -1,
-             apply_info->crosses_incognito, permission_check,
-             request->initiator()) == PermissionsData::ACCESS_ALLOWED;
+             apply_info->crosses_incognito,
+             permission_check) == PermissionsData::ACCESS_ALLOWED;
 }
 
 // static
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index a1b739f..ee2cf73c 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -1498,9 +1498,7 @@
           WebRequestPermissions::CanExtensionAccessURL(
               extension_info_map, listener->id.extension_id, url,
               frame_data.tab_id, crosses_incognito,
-              WebRequestPermissions::REQUIRE_HOST_PERMISSION,
-              request->initiator());
-
+              WebRequestPermissions::REQUIRE_HOST_PERMISSION);
       if (access != PermissionsData::ACCESS_ALLOWED) {
         if (access == PermissionsData::ACCESS_WITHHELD &&
             web_request_event_router_delegate_) {
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index ccfe22b..a683ec3 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -19,6 +19,7 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "net/url_request/url_request.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/login/login_state.h"
@@ -150,8 +151,7 @@
     const GURL& url,
     int tab_id,
     bool crosses_incognito,
-    HostPermissionsCheck host_permissions_check,
-    const base::Optional<url::Origin>& initiator) {
+    HostPermissionsCheck host_permissions_check) {
   // extension_info_map can be NULL in testing.
   if (!extension_info_map)
     return PermissionsData::ACCESS_ALLOWED;
@@ -161,12 +161,6 @@
   if (!extension)
     return PermissionsData::ACCESS_DENIED;
 
-  // Prevent viewing / modifying requests initiated by a host protected by
-  // policy.
-  if (initiator && extension->permissions_data()->IsRuntimeBlockedHost(
-                       initiator->GetPhysicalOrigin().GetURL()))
-    return PermissionsData::ACCESS_DENIED;
-
   // When we are in a Public Session, allow all URLs for webRequests initiated
   // by a regular extension (but don't allow chrome:// URLs).
 #if defined(OS_CHROMEOS)
diff --git a/extensions/browser/api/web_request/web_request_permissions.h b/extensions/browser/api/web_request/web_request_permissions.h
index 74d9475..ee6ba29 100644
--- a/extensions/browser/api/web_request/web_request_permissions.h
+++ b/extensions/browser/api/web_request/web_request_permissions.h
@@ -9,9 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/optional.h"
 #include "extensions/common/permissions/permissions_data.h"
-#include "url/origin.h"
 
 class GURL;
 
@@ -49,15 +47,14 @@
   static void AllowAllExtensionLocationsInPublicSessionForTesting(bool value);
 
   // |host_permission_check| controls how permissions are checked with regard to
-  // |url| and |initiator| if an initiator exists.
+  // |url|.
   static extensions::PermissionsData::AccessType CanExtensionAccessURL(
       const extensions::InfoMap* extension_info_map,
       const std::string& extension_id,
       const GURL& url,
       int tab_id,
       bool crosses_incognito,
-      HostPermissionsCheck host_permissions_check,
-      const base::Optional<url::Origin>& initiator);
+      HostPermissionsCheck host_permissions_check);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(WebRequestPermissions);
diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc
index 96e8269..245040e 100644
--- a/extensions/common/permissions/permissions_data.cc
+++ b/extensions/common/permissions/permissions_data.cc
@@ -280,7 +280,7 @@
 bool PermissionsData::HasHostPermission(const GURL& url) const {
   base::AutoLock auto_lock(runtime_lock_);
   return active_permissions_unsafe_->HasExplicitAccessToOrigin(url) &&
-         !IsRuntimeBlockedHostUnsafe(url);
+         !IsRuntimeBlockedHost(url);
 }
 
 bool PermissionsData::HasEffectiveAccessToAllHosts() const {
@@ -412,7 +412,7 @@
   return false;
 }
 
-bool PermissionsData::IsRuntimeBlockedHostUnsafe(const GURL& url) const {
+bool PermissionsData::IsRuntimeBlockedHost(const GURL& url) const {
   runtime_lock_.AssertAcquired();
   return PolicyBlockedHostsUnsafe().MatchesURL(url) &&
          !PolicyAllowedHostsUnsafe().MatchesURL(url);
@@ -431,7 +431,7 @@
     return ACCESS_DENIED;
 
   if (extension->location() != Manifest::COMPONENT &&
-      extension->permissions_data()->IsRuntimeBlockedHostUnsafe(document_url)) {
+      extension->permissions_data()->IsRuntimeBlockedHost(document_url)) {
     if (error)
       *error = extension_misc::kPolicyBlockedScripting;
     return ACCESS_DENIED;
diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h
index c1c5de2..a619590a 100644
--- a/extensions/common/permissions/permissions_data.h
+++ b/extensions/common/permissions/permissions_data.h
@@ -258,17 +258,16 @@
   // methods instead (e.g. CanAccessPage()).
   const URLPatternSet policy_allowed_hosts() const;
 
-  // Check if a specific URL is blocked by policy from extension use at runtime.
-  bool IsRuntimeBlockedHost(const GURL& url) const {
-    base::AutoLock auto_lock(runtime_lock_);
-    return IsRuntimeBlockedHostUnsafe(url);
-  }
-
 #if defined(UNIT_TEST)
   const PermissionSet* GetTabSpecificPermissionsForTesting(int tab_id) const {
     base::AutoLock auto_lock(runtime_lock_);
     return GetTabSpecificPermissions(tab_id);
   }
+
+  bool IsRuntimeBlockedHostForTesting(const GURL& url) const {
+    base::AutoLock auto_lock(runtime_lock_);
+    return IsRuntimeBlockedHost(url);
+  }
 #endif
 
  private:
@@ -297,8 +296,7 @@
                           std::string* error) const;
 
   // Check if a specific URL is blocked by policy from extension use at runtime.
-  // You must acquire the runtime_lock_ before calling.
-  bool IsRuntimeBlockedHostUnsafe(const GURL& url) const;
+  bool IsRuntimeBlockedHost(const GURL& url) const;
 
   // Same as policy_blocked_hosts but instead returns a reference.
   // You must acquire runtime_lock_ before calling this.
diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h
index 7b80a33..40fe3d57 100644
--- a/gpu/command_buffer/common/constants.h
+++ b/gpu/command_buffer/common/constants.h
@@ -8,6 +8,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "build/build_config.h"
+
 namespace gpu {
 
 typedef int32_t CommandBufferOffset;
@@ -68,8 +70,13 @@
 // Common Command Buffer shared memory transfer buffer ID.
 const int32_t kCommandBufferSharedMemoryId = 4;
 
-// The size to set for the program cache.
+// The size to set for the program cache for default and low-end device cases.
+#if !defined(OS_ANDROID)
 const size_t kDefaultMaxProgramCacheMemoryBytes = 6 * 1024 * 1024;
+#else
+const size_t kDefaultMaxProgramCacheMemoryBytes = 2 * 1024 * 1024;
+const size_t kLowEndMaxProgramCacheMemoryBytes = 512 * 1024;
+#endif
 
 // Namespace used to separate various command buffer types.
 enum CommandBufferNamespace : int8_t {
diff --git a/gpu/command_buffer/service/gpu_preferences.cc b/gpu/command_buffer/service/gpu_preferences.cc
index 06bc6b41..1b7983d 100644
--- a/gpu/command_buffer/service/gpu_preferences.cc
+++ b/gpu/command_buffer/service/gpu_preferences.cc
@@ -4,9 +4,16 @@
 
 #include "gpu/command_buffer/service/gpu_preferences.h"
 
+#include "base/sys_info.h"
+
 namespace gpu {
 
 GpuPreferences::GpuPreferences() {
+  gpu_program_cache_size = kDefaultMaxProgramCacheMemoryBytes;
+#if defined(OS_ANDROID)
+  if (base::SysInfo::IsLowEndDevice())
+    gpu_program_cache_size = kLowEndMaxProgramCacheMemoryBytes;
+#endif
 }
 
 GpuPreferences::GpuPreferences(const GpuPreferences& other) = default;
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index 93a0b221..79839eb 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -7,8 +7,10 @@
 #include <stddef.h>
 
 #include "base/base64.h"
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
@@ -220,7 +222,10 @@
           disable_program_caching_for_transform_feedback),
       curr_size_bytes_(0),
       store_(ProgramMRUCache::NO_AUTO_EVICT),
-      activity_flags_(activity_flags) {}
+      activity_flags_(activity_flags),
+      memory_pressure_listener_(
+          base::Bind(&MemoryProgramCache::HandleMemoryPressure,
+                     base::Unretained(this))) {}
 
 MemoryProgramCache::~MemoryProgramCache() {}
 
@@ -471,6 +476,23 @@
   }
 }
 
+void MemoryProgramCache::HandleMemoryPressure(
+    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+  // Set a low limit on cache size for MEMORY_PRESSURE_LEVEL_MODERATE.
+  size_t limit = max_size_bytes_ / 4;
+  if (memory_pressure_level ==
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+    limit = 0;
+  }
+  if (curr_size_bytes_ <= limit)
+    return;
+  size_t initial_size = curr_size_bytes_;
+  while (curr_size_bytes_ > limit && !store_.empty())
+    store_.Erase(store_.rbegin());
+  UMA_HISTOGRAM_COUNTS_100000("GPU.ProgramCache.MemoryReleasedOnPressure",
+                              (initial_size - curr_size_bytes_) / 1024);
+}
+
 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
     GLsizei length,
     GLenum format,
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
index 5cef31f..e085ff8e 100644
--- a/gpu/command_buffer/service/memory_program_cache.h
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -14,6 +14,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/containers/mru_cache.h"
 #include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
 #include "base/memory/ref_counted.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/program_cache.h"
@@ -166,6 +167,9 @@
   typedef base::MRUCache<std::string,
                          scoped_refptr<ProgramCacheValue> > ProgramMRUCache;
 
+  void HandleMemoryPressure(
+      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
   const size_t max_size_bytes_;
   const bool disable_gpu_shader_disk_cache_;
   const bool disable_program_caching_for_transform_feedback_;
@@ -173,6 +177,8 @@
   ProgramMRUCache store_;
   GpuProcessActivityFlags* activity_flags_;
 
+  base::MemoryPressureListener memory_pressure_listener_;
+
   DISALLOW_COPY_AND_ASSIGN(MemoryProgramCache);
 };
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index abbecf34..e7e1255 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -572,6 +572,10 @@
 // the user from interacting with the browser view.
 @property(nonatomic, strong)
     ActivityOverlayCoordinator* activityOverlayCoordinator;
+// A block to be run when the |tabWasAdded:| method completes the animation
+// for the presentation of a new tab. Can be used to record performance metrics.
+@property(nonatomic, strong, nullable)
+    ProceduralBlock foregroundTabWasAddedCompletionBlock;
 
 // The user agent type used to load the currently visible page. User agent type
 // is NONE if there is no visible page or visible page is a native page.
@@ -932,6 +936,8 @@
 @synthesize hideStatusBar = _hideStatusBar;
 @synthesize activityOverlayCoordinator = _activityOverlayCoordinator;
 @synthesize presenting = _presenting;
+@synthesize foregroundTabWasAddedCompletionBlock =
+    _foregroundTabWasAddedCompletionBlock;
 
 #pragma mark - Object lifecycle
 
@@ -1195,6 +1201,23 @@
 }
 
 - (void)newTab:(id)sender {
+  // Observe the timing of the new tab creation, both MainController
+  // and BrowserViewController call into this method on the correct BVC to
+  // create new tabs making it preferable to doing this in
+  // |chromeExecuteCommand:|.
+  NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
+  BOOL offTheRecord = self.isOffTheRecord;
+  self.foregroundTabWasAddedCompletionBlock = ^{
+    double duration = [NSDate timeIntervalSinceReferenceDate] - startTime;
+    base::TimeDelta timeDelta = base::TimeDelta::FromSecondsD(duration);
+    if (offTheRecord) {
+      UMA_HISTOGRAM_TIMES("Toolbar.Menu.NewIncognitoTabPresentationDuration",
+                          timeDelta);
+    } else {
+      UMA_HISTOGRAM_TIMES("Toolbar.Menu.NewTabPresentationDuration", timeDelta);
+    }
+  };
+
   [self setLastTapPoint:sender];
   DCHECK(self.visible || self.dismissingModal);
   Tab* currentTab = [_model currentTab];
@@ -1622,6 +1645,10 @@
             [self tabSelected:currentTab];
           }
           startVoiceSearchIfNecessaryBlock();
+
+          if (self.foregroundTabWasAddedCompletionBlock) {
+            self.foregroundTabWasAddedCompletionBlock();
+          }
         });
   } else {
     // -updateSnapshotWithOverlay will force a screen redraw, so take the
@@ -1660,6 +1687,9 @@
           startVoiceSearchIfNecessaryBlock();
         });
   }
+  // Reset the foreground tab completion block so that it can never be
+  // called more than once regardless of foreground/background tab appearances.
+  self.foregroundTabWasAddedCompletionBlock = nil;
 }
 
 #pragma mark - UI Configuration and Layout
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm b/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
index 814e302..3630dd38 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
@@ -49,6 +49,14 @@
 @property(nonatomic, strong)
     NSMutableDictionary<NSNumber*, EditorField*>* fieldsMap;
 
+// The reference to the autofill::CREDIT_CARD_EXP_MONTH field, if any (i.e if
+// we are dealing with a server card this will not exist).
+@property(nonatomic, strong) EditorField* creditCardExpMonthField;
+
+// The reference to the autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR field, if any
+// (i.e. if we are dealing with a server card this will not exist).
+@property(nonatomic, strong) EditorField* creditCardExpYearField;
+
 @end
 
 @implementation CreditCardEditViewControllerMediator
@@ -59,6 +67,8 @@
 @synthesize paymentRequest = _paymentRequest;
 @synthesize creditCard = _creditCard;
 @synthesize fieldsMap = _fieldsMap;
+@synthesize creditCardExpMonthField = _creditCardExpMonthField;
+@synthesize creditCardExpYearField = _creditCardExpYearField;
 
 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest
                             creditCard:(autofill::CreditCard*)creditCard {
@@ -83,7 +93,14 @@
 
 - (void)setConsumer:(id<PaymentRequestEditConsumer>)consumer {
   _consumer = consumer;
+
   [self.consumer setEditorFields:[self createEditorFields]];
+  if (self.creditCardExpMonthField) {
+    [self loadMonths];
+  }
+  if (self.creditCardExpYearField) {
+    [self loadYears];
+  }
 }
 
 - (void)setBillingProfile:(autofill::AutofillProfile*)billingProfile {
@@ -155,6 +172,55 @@
 
 #pragma mark - Helper methods
 
+// Queries the month numbers.
+- (void)loadMonths {
+  NSMutableArray<NSString*>* months = [[NSMutableArray alloc] init];
+
+  for (int month = 1; month <= 12; month++) {
+    NSString* monthString = [NSString stringWithFormat:@"%02d", month];
+    [months addObject:monthString];
+  }
+
+  // Notify the view controller asynchronously to allow for the view to update.
+  __weak CreditCardEditViewControllerMediator* weakSelf = self;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    [weakSelf.consumer setOptions:months
+                   forEditorField:weakSelf.creditCardExpMonthField];
+  });
+}
+
+// Queries the year numbers.
+- (void)loadYears {
+  NSMutableArray<NSString*>* years = [[NSMutableArray alloc] init];
+
+  NSDateComponents* dateComponents =
+      [[NSCalendar currentCalendar] components:NSCalendarUnitYear
+                                      fromDate:[NSDate date]];
+
+  int currentYear = [dateComponents year];
+  bool foundCardExpirationYear = false;
+  for (int year = currentYear; year < currentYear + 10; year++) {
+    NSString* yearString = [NSString stringWithFormat:@"%04d", year];
+    if ([yearString isEqualToString:_creditCardExpYearField.value])
+      foundCardExpirationYear = true;
+    [years addObject:yearString];
+  }
+
+  // Ensure that the expiration year on a user's saved card is
+  // always available as one of the options to select from. This is
+  // useful in the case that the user's card is expired.
+  if (!foundCardExpirationYear) {
+    [years insertObject:_creditCardExpYearField.value atIndex:0];
+  }
+
+  // Notify the view controller asynchronously to allow for the view to update.
+  __weak CreditCardEditViewControllerMediator* weakSelf = self;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    [weakSelf.consumer setOptions:years
+                   forEditorField:weakSelf.creditCardExpYearField];
+  });
+}
+
 - (NSArray<EditorField*>*)createEditorFields {
   NSMutableArray<EditorField*>* fields = [[NSMutableArray alloc] init];
 
@@ -220,11 +286,18 @@
   }
   [fields addObject:creditCardNameField];
 
+  NSDateComponents* dateComponents = [[NSCalendar currentCalendar]
+      components:NSCalendarUnitMonth | NSCalendarUnitYear
+        fromDate:[NSDate date]];
+
+  int currentMonth = [dateComponents month];
+
   // Expiration month field.
   NSString* creditCardExpMonth =
-      _creditCard
+      _creditCard && (_creditCard->expiration_month() >= 1 &&
+                      _creditCard->expiration_month() <= 12)
           ? [NSString stringWithFormat:@"%02d", _creditCard->expiration_month()]
-          : nil;
+          : [NSString stringWithFormat:@"%02d", currentMonth];
   fieldKey = [NSNumber numberWithInt:AutofillUITypeCreditCardExpMonth];
   EditorField* expirationMonthField = self.fieldsMap[fieldKey];
   if (!expirationMonthField) {
@@ -236,13 +309,15 @@
                       required:YES];
     [self.fieldsMap setObject:expirationMonthField forKey:fieldKey];
   }
+  self.creditCardExpMonthField = expirationMonthField;
   [fields addObject:expirationMonthField];
 
   // Expiration year field.
+  int currentYear = [dateComponents year];
   NSString* creditCardExpYear =
       _creditCard
           ? [NSString stringWithFormat:@"%04d", _creditCard->expiration_year()]
-          : nil;
+          : [NSString stringWithFormat:@"%04d", currentYear];
   fieldKey = [NSNumber numberWithInt:AutofillUITypeCreditCardExpYear];
   EditorField* expirationYearField = self.fieldsMap[fieldKey];
   if (!expirationYearField) {
@@ -254,6 +329,7 @@
                       required:YES];
     [self.fieldsMap setObject:expirationYearField forKey:fieldKey];
   }
+  self.creditCardExpYearField = expirationYearField;
   [fields addObject:expirationYearField];
 
   // The billing address field appears after the expiration year field.
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
index cdb4ba2b..6b2a75f6 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
@@ -2025,6 +2025,10 @@
 - (Tab*)dismissWithNewTabAnimation:(const GURL&)URL
                            atIndex:(NSUInteger)position
                         transition:(ui::PageTransition)transition {
+  // Record the start time for this operation so it may be reported as a metric
+  // in the animation completion block.
+  NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
+
   // This helps smooth out the animation.
   [[_scrollView layer] setShouldRasterize:YES];
   if (_isBeingDismissed)
@@ -2077,6 +2081,15 @@
     [newCard removeFromSuperview];
     [[_scrollView layer] setShouldRasterize:NO];
     [_delegate tabSwitcherDismissTransitionDidEnd:self];
+    double duration = [NSDate timeIntervalSinceReferenceDate] - startTime;
+    if (_activeCardSet.tabModel.isOffTheRecord) {
+      UMA_HISTOGRAM_TIMES(
+          "Toolbar.TabSwitcher.NewIncognitoTabPresentationDurationn",
+          base::TimeDelta::FromSecondsD(duration));
+    } else {
+      UMA_HISTOGRAM_TIMES("Toolbar.TabSwitcher.NewTabPresentationDuration",
+                          base::TimeDelta::FromSecondsD(duration));
+    }
   };
 
   CGPoint origin = _lastTapPoint;
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index a4c5a309e..3c5689f 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -196,6 +196,7 @@
     "null_video_sink.h",
     "output_device_info.cc",
     "output_device_info.h",
+    "overlay_info.cc",
     "overlay_info.h",
     "pipeline.h",
     "pipeline_impl.cc",
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 9ab0dbc0..70ece28 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -230,7 +230,7 @@
 
 // CanPlayThrough issued according to standard.
 const base::Feature kSpecCompliantCanPlayThrough{
-    "SpecCompliantCanPlayThrough", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SpecCompliantCanPlayThrough", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Use shared block-based buffering for media.
 const base::Feature kUseNewMediaCache{"use-new-media-cache",
diff --git a/media/base/overlay_info.cc b/media/base/overlay_info.cc
new file mode 100644
index 0000000..1658f2a
--- /dev/null
+++ b/media/base/overlay_info.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/overlay_info.h"
+#include "media/base/surface_manager.h"
+
+namespace media {
+
+OverlayInfo::OverlayInfo() = default;
+OverlayInfo::OverlayInfo(const OverlayInfo&) = default;
+
+bool OverlayInfo::HasValidSurfaceId() const {
+  return surface_id != SurfaceManager::kNoSurfaceID;
+}
+
+bool OverlayInfo::HasValidRoutingToken() const {
+  return routing_token.has_value();
+}
+
+}  // namespace media
diff --git a/media/base/overlay_info.h b/media/base/overlay_info.h
index da0c387..089edca 100644
--- a/media/base/overlay_info.h
+++ b/media/base/overlay_info.h
@@ -9,13 +9,38 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/unguessable_token.h"
+#include "media/base/media_export.h"
+#include "media/base/surface_manager.h"
 
 namespace media {
 
-// Request information to construct an overlay.  This can be either a surface_id
-// or an AndroidOverlay routing token.
-using ProvideOverlayInfoCB =
-    base::Callback<void(int, const base::Optional<base::UnguessableToken>&)>;
+struct MEDIA_EXPORT OverlayInfo {
+  // An unset routing token indicates "do not use any routing token".  A null
+  // routing token isn't serializable, else we'd probably use that instead.
+  using RoutingToken = base::Optional<base::UnguessableToken>;
+
+  OverlayInfo();
+  OverlayInfo(const OverlayInfo&);
+
+  // Convenience functions to return true if and only if this specifies a
+  // surface ID / routing token that is not kNoSurfaceID / empty.  I.e., if we
+  // provide enough info to create an overlay.
+  bool HasValidSurfaceId() const;
+  bool HasValidRoutingToken() const;
+
+  // This is the SurfaceManager surface id, or SurfaceManager::kNoSurfaceID to
+  // indicate that no surface from SurfaceManager should be used.
+  int surface_id = SurfaceManager::kNoSurfaceID;
+
+  // The routing token for AndroidOverlay, if any.
+  RoutingToken routing_token;
+
+  // Is the player in fullscreen?
+  bool is_fullscreen = false;
+};
+
+// Request OverlayInformation.
+using ProvideOverlayInfoCB = base::Callback<void(const OverlayInfo&)>;
 using RequestOverlayInfoCB =
     base::Callback<void(bool, const ProvideOverlayInfoCB&)>;
 
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 7f21f07..a993c41 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -260,7 +260,7 @@
       embedded_media_experience_enabled_(
           params->embedded_media_experience_enabled()),
       request_routing_token_cb_(params->request_routing_token_cb()),
-      overlay_routing_token_(base::UnguessableToken()) {
+      overlay_routing_token_(OverlayInfo::RoutingToken()) {
   DVLOG(1) << __func__;
   DCHECK(!adjust_allocated_memory_cb_.is_null());
   DCHECK(renderer_factory_selector_);
@@ -366,7 +366,7 @@
                                               surface_created_cb_.callback());
   } else if (request_routing_token_cb_ &&
              overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
-    overlay_routing_token_.reset();
+    overlay_routing_token_is_pending_ = true;
     token_available_cb_.Reset(
         base::Bind(&WebMediaPlayerImpl::OnOverlayRoutingToken, AsWeakPtr()));
     request_routing_token_cb_.Run(token_available_cb_.callback());
@@ -387,7 +387,8 @@
     overlay_surface_id_ = SurfaceManager::kNoSurfaceID;
   } else if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
     token_available_cb_.Cancel();
-    overlay_routing_token_ = base::UnguessableToken();
+    overlay_routing_token_is_pending_ = false;
+    overlay_routing_token_ = OverlayInfo::RoutingToken();
   }
 
   if (decoder_requires_restart_for_overlay_)
@@ -397,6 +398,8 @@
 }
 
 void WebMediaPlayerImpl::EnteredFullscreen() {
+  overlay_info_.is_fullscreen = true;
+
   // |force_video_overlays_| implies that we're already in overlay mode, so take
   // no action here.  Otherwise, switch to an overlay if it's allowed and if
   // it will display properly.
@@ -407,11 +410,20 @@
   if (observer_)
     observer_->OnEnteredFullscreen();
 
-  // TODO(liberato): if the decoder provided a callback for fullscreen state,
-  // then notify it now.
+  // We send this only if we can send multiple calls.  Otherwise, either (a)
+  // we already sent it and we don't have a callback anyway (we reset it when
+  // it's called in restart mode), or (b) we'll send this later when the surface
+  // actually arrives.  GVD assumes that the first overlay info will have the
+  // routing information.  Note that we set |is_fullscreen_| earlier, so that
+  // if EnableOverlay() can include fullscreen info in case it sends the overlay
+  // info before returning.
+  if (!decoder_requires_restart_for_overlay_)
+    MaybeSendOverlayInfoToDecoder();
 }
 
 void WebMediaPlayerImpl::ExitedFullscreen() {
+  overlay_info_.is_fullscreen = false;
+
   // If we're in overlay mode, then exit it unless we're supposed to be in
   // overlay mode all the time.
   if (!force_video_overlays_ && overlay_enabled_)
@@ -419,8 +431,9 @@
   if (observer_)
     observer_->OnExitedFullscreen();
 
-  // TODO(liberato): if the decoder provided a callback for fullscreen state,
-  // then notify it now.
+  // See EnteredFullscreen for why we do this.
+  if (!decoder_requires_restart_for_overlay_)
+    MaybeSendOverlayInfoToDecoder();
 }
 
 void WebMediaPlayerImpl::BecameDominantVisibleContent(bool isDominant) {
@@ -1747,7 +1760,9 @@
 void WebMediaPlayerImpl::OnOverlayRoutingToken(
     const base::UnguessableToken& token) {
   DCHECK(overlay_mode_ == OverlayMode::kUseAndroidOverlay);
-  overlay_routing_token_ = token;
+  // TODO(liberato): |token| should already be a RoutingToken.
+  overlay_routing_token_is_pending_ = false;
+  overlay_routing_token_ = OverlayInfo::RoutingToken(token);
   MaybeSendOverlayInfoToDecoder();
 }
 
@@ -1795,37 +1810,27 @@
   // using overlays.  Assuming that the decoder has requested info, the only
   // case in which we don't want to send something is if we've requested the
   // info but not received it yet.  Then, we should wait until we do.
+  //
+  // Initialization requires this; AVDA should start with enough info to make an
+  // overlay, so that (pre-M) the initial codec is created with the right output
+  // surface; it can't switch later.
   if (overlay_mode_ == OverlayMode::kUseContentVideoView) {
     if (!overlay_surface_id_.has_value())
       return;
+
+    overlay_info_.surface_id = *overlay_surface_id_;
   } else if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
-    if (!overlay_routing_token_.has_value())
+    if (overlay_routing_token_is_pending_)
       return;
+
+    overlay_info_.routing_token = overlay_routing_token_;
   }
 
-  // Note that we're guaranteed that both |overlay_surface_id_| and
-  // |overlay_routing_token_| have values, since both have values unless there
-  // is a request pending.  Nobody calls us if a request is pending.
-
-  int surface_id = SurfaceManager::kNoSurfaceID;
-  if (overlay_surface_id_)
-    surface_id = *overlay_surface_id_;
-
-  // Since we represent "no token" as a null UnguessableToken, we translate it
-  // into an optional here.  Alternatively, we could represent it as a
-  // base::Optional in |overlay_routing_token_|, but then we'd have a
-  // base::Optional<base::Optional<base::UnguessableToken> >.  We don't do that
-  // because... just because.
-  base::Optional<base::UnguessableToken> routing_token;
-  if (overlay_routing_token_.has_value() && !overlay_routing_token_->is_empty())
-    routing_token = *overlay_routing_token_;
-
   // If restart is required, the callback is one-shot only.
   if (decoder_requires_restart_for_overlay_) {
-    base::ResetAndReturn(&provide_overlay_info_cb_)
-        .Run(surface_id, routing_token);
+    base::ResetAndReturn(&provide_overlay_info_cb_).Run(overlay_info_);
   } else {
-    provide_overlay_info_cb_.Run(surface_id, routing_token);
+    provide_overlay_info_cb_.Run(overlay_info_);
   }
 }
 
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index df65ecb..5bdf19e 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -260,8 +260,11 @@
   // AndroidOverlay, this is the routing token.
   bool HaveOverlayInfo();
 
-  // Send the overlay surface ID / routing token to the decoder if we have it
-  // and if it has been requested.
+  // Send OverlayInfo to the decoder.
+  //
+  // If we've requested but not yet received the surface id or routing token, or
+  // if there's no decoder-provided callback to send the overlay info, then this
+  // call will do nothing.
   void MaybeSendOverlayInfoToDecoder();
 
   void OnPipelineSuspended();
@@ -776,11 +779,15 @@
   // Optional callback to request the routing token for AndroidOverlay.
   RequestRoutingTokenCallback request_routing_token_cb_;
 
-  // Routing token, if we have one.  No value if we have a request pending to
-  // get it via |request_routing_token_cb_|.  A has_value() is_empty() token
-  // indicates that we requested and received an empty token.  Note that we
-  // can't send an empty token via IPC, so we handle that specially.
-  base::Optional<base::UnguessableToken> overlay_routing_token_;
+  // If |overlay_routing_token_is_pending_| is false, then
+  // |overlay_routing_token_| contains the routing token we should send, if any.
+  // Otherwise, |overlay_routing_token_| is undefined.  We set the flag while
+  // we have a request for the token that hasn't been answered yet; i.e., it
+  // means that we don't know what, if any, token we should be using.
+  bool overlay_routing_token_is_pending_ = false;
+  OverlayInfo::RoutingToken overlay_routing_token_;
+
+  OverlayInfo overlay_info_;
 
   DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
 };
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index c9b7fb10..308ccb7 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -307,8 +307,7 @@
   }
 
   // If external surfaces are not supported we can complete initialization now.
-  CompleteInitialization(SurfaceManager::kNoSurfaceID,
-                         base::UnguessableToken());
+  CompleteInitialization(OverlayInfo());
 }
 
 // OnOverlayInfoAvailable() might be called at any time between Initialize() and
@@ -316,9 +315,7 @@
 // the current state.
 // At most one of |surface_id| and |token| should be provided.  The other will
 // be kNoSurfaceID or an empty token, respectively.
-void GpuVideoDecoder::OnOverlayInfoAvailable(
-    int surface_id,
-    const base::Optional<base::UnguessableToken>& token) {
+void GpuVideoDecoder::OnOverlayInfoAvailable(const OverlayInfo& overlay_info) {
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
   if (!vda_)
@@ -329,18 +326,16 @@
   // SetSurface() before initializing because there is no remote VDA to handle
   // the call yet.
   if (!vda_initialized_) {
-    CompleteInitialization(surface_id, token);
+    CompleteInitialization(overlay_info);
     return;
   }
 
   // The VDA must be already initialized (or async initialization is in
   // progress) so we can call SetSurface().
-  vda_->SetSurface(surface_id, token);
+  vda_->SetOverlayInfo(overlay_info);
 }
 
-void GpuVideoDecoder::CompleteInitialization(
-    int surface_id,
-    const base::Optional<base::UnguessableToken>& token) {
+void GpuVideoDecoder::CompleteInitialization(const OverlayInfo& overlay_info) {
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
   DCHECK(vda_);
   DCHECK(!init_cb_.is_null());
@@ -349,8 +344,7 @@
   VideoDecodeAccelerator::Config vda_config;
   vda_config.profile = config_.profile();
   vda_config.cdm_id = cdm_id_;
-  vda_config.surface_id = surface_id;
-  vda_config.overlay_routing_token = token;
+  vda_config.overlay_info = overlay_info;
   vda_config.encryption_scheme = config_.encryption_scheme();
   vda_config.is_deferred_initialization_allowed = true;
   vda_config.initial_expected_coded_size = config_.coded_size();
diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h
index 604f6ad..634a46a 100644
--- a/media/filters/gpu_video_decoder.h
+++ b/media/filters/gpu_video_decoder.h
@@ -155,17 +155,13 @@
 
   // Provided to the |request_overlay_info_cb_| callback given during
   // construction.  Sets or changes the output surface.
-  void OnOverlayInfoAvailable(
-      int surface_id,
-      const base::Optional<base::UnguessableToken>& routing_token);
+  void OnOverlayInfoAvailable(const OverlayInfo& overlay_info);
 
   // If the VDA supports external surfaces, we must wait for the surface before
   // completing initialization. This will be called by OnSurfaceAvailable() once
   // the surface is known or immediately by Initialize() if external surfaces
   // are unsupported.
-  void CompleteInitialization(
-      int surface_id,
-      const base::Optional<base::UnguessableToken>& token);
+  void CompleteInitialization(const OverlayInfo& overlay_info);
 
   bool needs_bitstream_conversion_;
 
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index dd1061f4..fec2ce78 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -113,8 +113,7 @@
 // software fallback exists.
 bool ShouldDeferSurfaceCreation(
     AVDACodecAllocator* codec_allocator,
-    int surface_id,
-    base::Optional<base::UnguessableToken> overlay_routing_token,
+    const OverlayInfo& overlay_info,
     VideoCodec codec,
     const AndroidVideoDecodeAccelerator::PlatformConfig& platform_config) {
   if (platform_config.force_deferred_surface_creation)
@@ -122,7 +121,7 @@
 
   // TODO(liberato): We might still want to defer if we've got a routing
   // token.  It depends on whether we want to use it right away or not.
-  if (surface_id != SurfaceManager::kNoSurfaceID || overlay_routing_token)
+  if (overlay_info.HasValidSurfaceId() || overlay_info.HasValidRoutingToken())
     return false;
 
   return codec == kCodecH264 && codec_allocator->IsAnyRegisteredAVDA() &&
@@ -359,12 +358,11 @@
 
   // If we're low on resources, we may decide to defer creation of the surface
   // until the codec is actually used.
-  if (ShouldDeferSurfaceCreation(codec_allocator_, config_.surface_id,
-                                 config_.overlay_routing_token,
+  if (ShouldDeferSurfaceCreation(codec_allocator_, config_.overlay_info,
                                  codec_config_->codec, platform_config_)) {
     // We should never be here if a SurfaceView is required.
     // TODO(liberato): This really isn't true with AndroidOverlay.
-    DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID);
+    DCHECK(!config_.overlay_info.HasValidSurfaceId());
     defer_surface_creation_ = true;
   }
 
@@ -416,18 +414,24 @@
   // surface creation for other reasons, in which case the sync path with just
   // signal success optimistically.
   if (during_initialize_ && !deferred_initialization_pending_) {
-    DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID);
-    DCHECK(!config_.overlay_routing_token);
+    DCHECK(!config_.overlay_info.HasValidSurfaceId());
+    DCHECK(!config_.overlay_info.HasValidRoutingToken());
     OnSurfaceTransition(nullptr);
     return;
   }
 
-  // If we have a surface, then notify |surface_chooser_| about it.
+  // If we have a surface, then notify |surface_chooser_| about it.  If we were
+  // told not to use an overlay (kNoSurfaceID or a null routing token), then we
+  // leave the factory blank.
   AndroidOverlayFactoryCB factory;
-  if (config_.surface_id != SurfaceManager::kNoSurfaceID)
-    factory = base::Bind(&CreateContentVideoViewOverlay, config_.surface_id);
-  else if (config_.overlay_routing_token && overlay_factory_cb_)
-    factory = base::Bind(overlay_factory_cb_, *config_.overlay_routing_token);
+  if (config_.overlay_info.HasValidSurfaceId()) {
+    factory = base::Bind(&CreateContentVideoViewOverlay,
+                         config_.overlay_info.surface_id);
+  } else if (config_.overlay_info.HasValidRoutingToken() &&
+             overlay_factory_cb_) {
+    factory =
+        base::Bind(overlay_factory_cb_, *config_.overlay_info.routing_token);
+  }
 
   // Notify |surface_chooser_| that we've started.  This guarantees that we'll
   // get a callback.  It might not be a synchronous callback, but we're not in
@@ -1250,28 +1254,42 @@
   StartCodecDrain(DRAIN_FOR_RESET);
 }
 
-void AndroidVideoDecodeAccelerator::SetSurface(
-    int32_t surface_id,
-    const base::Optional<base::UnguessableToken>& routing_token) {
+void AndroidVideoDecodeAccelerator::SetOverlayInfo(
+    const OverlayInfo& overlay_info) {
   DVLOG(1) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  // It's possible that we'll receive a SetSurface before initializing the
-  // surface chooser.  For example, if we defer surface creation, then we'll
-  // signal success to WMPI before initializing it.  WMPI is free to change the
-  // surface.  In this case, just pretend that |surface_id| is the initial one.
-  if (state_ == BEFORE_OVERLAY_INIT) {
-    config_.surface_id = surface_id;
-    config_.overlay_routing_token = routing_token;
+  // Update |config_| to contain the most recent info.  Also save a copy, so
+  // that we can check for duplicate info later.
+  OverlayInfo previous_info = config_.overlay_info;
+  config_.overlay_info = overlay_info;
+
+  // It's possible that we'll receive SetSurface before initializing the surface
+  // chooser.  For example, if we defer surface creation, then we'll signal
+  // success to WMPI before initializing it.  WMPI is then free to change
+  // |surface_id|.  In this case, take no additional action, since |config_| is
+  // up to date.  We'll use it later.
+  if (state_ == BEFORE_OVERLAY_INIT)
+    return;
+
+  // Note that these might be kNoSurfaceID / empty.  In that case, we will
+  // revoke the factory.
+  int32_t surface_id = overlay_info.surface_id;
+  OverlayInfo::RoutingToken routing_token = overlay_info.routing_token;
+
+  // We don't want to change the factory unless this info has actually changed.
+  // We'll get the same info many times if some other part of the config is now
+  // different, such as fullscreen state.
+  if (surface_id == previous_info.surface_id &&
+      routing_token == previous_info.routing_token) {
     return;
   }
 
   AndroidOverlayFactoryCB factory;
-  if (routing_token && overlay_factory_cb_) {
+  if (routing_token && overlay_factory_cb_)
     factory = base::Bind(overlay_factory_cb_, *routing_token);
-  } else if (surface_id != SurfaceManager::kNoSurfaceID) {
+  else if (surface_id != SurfaceManager::kNoSurfaceID)
     factory = base::Bind(&CreateContentVideoViewOverlay, surface_id);
-  }
 
   surface_chooser_->ReplaceOverlayFactory(std::move(factory));
 }
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h
index f1e6c15..6f1458447 100644
--- a/media/gpu/android_video_decode_accelerator.h
+++ b/media/gpu/android_video_decode_accelerator.h
@@ -79,9 +79,7 @@
   void ReusePictureBuffer(int32_t picture_buffer_id) override;
   void Flush() override;
   void Reset() override;
-  void SetSurface(
-      int32_t surface_id,
-      const base::Optional<base::UnguessableToken>& routing_token) override;
+  void SetOverlayInfo(const OverlayInfo& overlay_info) override;
   void Destroy() override;
   bool TryToSetupDecodeOnSeparateThread(
       const base::WeakPtr<Client>& decode_client,
diff --git a/media/gpu/android_video_decode_accelerator_unittest.cc b/media/gpu/android_video_decode_accelerator_unittest.cc
index b8468b40..3a8f8b82 100644
--- a/media/gpu/android_video_decode_accelerator_unittest.cc
+++ b/media/gpu/android_video_decode_accelerator_unittest.cc
@@ -185,7 +185,7 @@
   // Initialize |vda_|, providing a new surface for it.  You may get the surface
   // by asking |codec_allocator_|.
   void InitializeAVDAWithOverlay() {
-    config_.surface_id = 123;
+    config_.overlay_info.surface_id = 123;
     ASSERT_TRUE(InitializeAVDA());
     base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(chooser_->factory_);
@@ -336,7 +336,7 @@
   // in a state that AVDA isn't supposed to handle (e.g., if we give it a
   // surface, then it would never decide to defer surface creation).
   platform_config_.force_deferred_surface_creation = true;
-  config_.surface_id = SurfaceManager::kNoSurfaceID;
+  config_.overlay_info.surface_id = SurfaceManager::kNoSurfaceID;
 
   EXPECT_CALL(*chooser_, MockInitialize()).Times(0);
   EXPECT_CALL(client_, NotifyInitializationComplete(true));
@@ -493,6 +493,7 @@
   // doesn't get confused about which surface is in use.  So, we assume that it
   // doesn't signal an error, and we check that it releases the right surface
   // with the codec.
+  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
   EXPECT_CALL(client_, NotifyError(_)).Times(0);
 
   platform_config_.allow_setsurface = false;
@@ -508,6 +509,7 @@
        OnSurfaceDestroyedWithoutSetSurfaceFreesTheCodec) {
   // If AVDA receives OnSurfaceDestroyed without support for SetSurface, then it
   // should free the codec.
+  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
   platform_config_.allow_setsurface = false;
   InitializeAVDAWithOverlay();
   EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0);
@@ -541,4 +543,29 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(AndroidVideoDecodeAcceleratorTest,
+       OverlayInfoWithDuplicateSurfaceIDDoesntChangeTheFactory) {
+  // Send OverlayInfo with duplicate info, and verify that it doesn't change
+  // the factory.
+  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
+  InitializeAVDAWithOverlay();
+
+  EXPECT_CALL(*chooser_, MockReplaceOverlayFactory()).Times(0);
+  OverlayInfo overlay_info = config_.overlay_info;
+  avda()->SetOverlayInfo(overlay_info);
+}
+
+TEST_F(AndroidVideoDecodeAcceleratorTest,
+       OverlayInfoWithNewSurfaceIDDoesChangeTheFactory) {
+  // Send OverlayInfo with new surface info, and verify that it does change the
+  // overlay factory.
+  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
+  InitializeAVDAWithOverlay();
+
+  EXPECT_CALL(*chooser_, MockReplaceOverlayFactory()).Times(1);
+  OverlayInfo overlay_info = config_.overlay_info;
+  overlay_info.surface_id++;
+  avda()->SetOverlayInfo(overlay_info);
+}
+
 }  // namespace media
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index 0fdf3d6c..47b9343 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -1180,12 +1180,12 @@
 
   main_thread_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&DXVAVideoDecodeAccelerator::NotifyResetDone, weak_ptr_));
-  main_thread_task_runner_->PostTask(
-      FROM_HERE,
       base::Bind(&DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped,
                  weak_ptr_, std::move(pending_input_buffers_)));
   pending_input_buffers_.clear();
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DXVAVideoDecodeAccelerator::NotifyResetDone, weak_ptr_));
 
   RETURN_AND_NOTIFY_ON_FAILURE(StartDecoderThread(),
                                "Failed to start decoder thread.",
diff --git a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
index e0cc707e..f8c85d5 100644
--- a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
+++ b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
@@ -169,14 +169,13 @@
   Send(new AcceleratedVideoDecoderMsg_Reset(decoder_route_id_));
 }
 
-void GpuVideoDecodeAcceleratorHost::SetSurface(
-    int32_t surface_id,
-    const base::Optional<base::UnguessableToken>& routing_token) {
+void GpuVideoDecodeAcceleratorHost::SetOverlayInfo(
+    const OverlayInfo& overlay_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!channel_)
     return;
-  Send(new AcceleratedVideoDecoderMsg_SetSurface(decoder_route_id_, surface_id,
-                                                 routing_token));
+  Send(new AcceleratedVideoDecoderMsg_SetOverlayInfo(decoder_route_id_,
+                                                     overlay_info));
 }
 
 void GpuVideoDecodeAcceleratorHost::Destroy() {
diff --git a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.h b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.h
index c80f4629..48351b1 100644
--- a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.h
+++ b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.h
@@ -47,8 +47,7 @@
   void ReusePictureBuffer(int32_t picture_buffer_id) override;
   void Flush() override;
   void Reset() override;
-  void SetSurface(int32_t surface_id,
-                  const base::Optional<base::UnguessableToken>& token) override;
+  void SetOverlayInfo(const OverlayInfo&) override;
   void Destroy() override;
 
   // gpu::CommandBufferProxyImpl::DeletionObserver implemetnation.
diff --git a/media/gpu/ipc/common/media_messages.h b/media/gpu/ipc/common/media_messages.h
index 506c391..2c56e251 100644
--- a/media/gpu/ipc/common/media_messages.h
+++ b/media/gpu/ipc/common/media_messages.h
@@ -12,6 +12,7 @@
 #include "gpu/ipc/common/gpu_param_traits_macros.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/param_traits_macros.h"
+#include "media/base/overlay_info.h"
 #include "media/gpu/ipc/common/media_param_traits.h"
 #include "media/video/jpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
@@ -92,11 +93,9 @@
 // Send reset request to the decoder.
 IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderMsg_Reset)
 
-// Send a surface id to the decoder.
-IPC_MESSAGE_ROUTED2(
-    AcceleratedVideoDecoderMsg_SetSurface,
-    int32_t,                                 /* Surface ID */
-    base::Optional<base::UnguessableToken>); /* AndroidOverlay routing token */
+// Send overlay info to the decoder.
+IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_SetOverlayInfo,
+                    media::OverlayInfo);
 
 // Send destroy request to the decoder.
 IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderMsg_Destroy)
diff --git a/media/gpu/ipc/common/media_param_traits_macros.h b/media/gpu/ipc/common/media_param_traits_macros.h
index b77189c..3fcbefd 100644
--- a/media/gpu/ipc/common/media_param_traits_macros.h
+++ b/media/gpu/ipc/common/media_param_traits_macros.h
@@ -8,6 +8,7 @@
 #include "gpu/config/gpu_info.h"
 #include "ipc/ipc_message_macros.h"
 #include "media/base/ipc/media_param_traits.h"
+#include "media/base/overlay_info.h"
 #include "media/gpu/ipc/common/create_video_encoder_params.h"
 #include "media/video/jpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
@@ -25,7 +26,7 @@
   IPC_STRUCT_TRAITS_MEMBER(encryption_scheme)
   IPC_STRUCT_TRAITS_MEMBER(cdm_id)
   IPC_STRUCT_TRAITS_MEMBER(is_deferred_initialization_allowed)
-  IPC_STRUCT_TRAITS_MEMBER(surface_id)
+  IPC_STRUCT_TRAITS_MEMBER(overlay_info)
   IPC_STRUCT_TRAITS_MEMBER(initial_expected_coded_size)
   IPC_STRUCT_TRAITS_MEMBER(supported_output_formats)
   IPC_STRUCT_TRAITS_MEMBER(sps)
@@ -41,4 +42,10 @@
   IPC_STRUCT_TRAITS_MEMBER(encoder_route_id)
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(media::OverlayInfo)
+  IPC_STRUCT_TRAITS_MEMBER(surface_id)
+  IPC_STRUCT_TRAITS_MEMBER(routing_token)
+  IPC_STRUCT_TRAITS_MEMBER(is_fullscreen)
+IPC_STRUCT_TRAITS_END()
+
 #endif  // MEDIA_GPU_IPC_COMMON_MEDIA_PARAM_TRAITS_MACROS_H_
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index 192f12e..0394ce47 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -210,7 +210,8 @@
                         OnReusePictureBuffer)
     IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Flush, OnFlush)
     IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Reset, OnReset)
-    IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_SetSurface, OnSetSurface)
+    IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_SetOverlayInfo,
+                        OnSetOverlayInfo)
     IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Destroy, OnDestroy)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
@@ -510,11 +511,10 @@
   video_decode_accelerator_->Reset();
 }
 
-void GpuVideoDecodeAccelerator::OnSetSurface(
-    int32_t surface_id,
-    const base::Optional<base::UnguessableToken>& routing_token) {
+void GpuVideoDecodeAccelerator::OnSetOverlayInfo(
+    const OverlayInfo& overlay_info) {
   DCHECK(video_decode_accelerator_);
-  video_decode_accelerator_->SetSurface(surface_id, routing_token);
+  video_decode_accelerator_->SetOverlayInfo(overlay_info);
 }
 
 void GpuVideoDecodeAccelerator::OnDestroy() {
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.h b/media/gpu/ipc/service/gpu_video_decode_accelerator.h
index db2c79d..53ce55b 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.h
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.h
@@ -98,9 +98,7 @@
   void OnReusePictureBuffer(int32_t picture_buffer_id);
   void OnFlush();
   void OnReset();
-  void OnSetSurface(
-      int32_t surface_id,
-      const base::Optional<base::UnguessableToken>& routing_token);
+  void OnSetOverlayInfo(const OverlayInfo& overlay_info);
   void OnDestroy();
 
   // Called on IO thread when |filter_| has been removed.
diff --git a/media/video/video_decode_accelerator.cc b/media/video/video_decode_accelerator.cc
index bc37cca..873e847 100644
--- a/media/video/video_decode_accelerator.cc
+++ b/media/video/video_decode_accelerator.cc
@@ -45,10 +45,8 @@
   NOTREACHED() << "Buffer import not supported.";
 }
 
-void VideoDecodeAccelerator::SetSurface(
-    int32_t surface_id,
-    const base::Optional<base::UnguessableToken>& routing_token) {
-  NOTREACHED() << "Surfaces are not supported.";
+void VideoDecodeAccelerator::SetOverlayInfo(const OverlayInfo& overlay_info) {
+  NOTREACHED() << "Overlays are not supported.";
 }
 
 GLenum VideoDecodeAccelerator::GetSurfaceInternalFormat() const {
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h
index e61f6e2..b37a85e 100644
--- a/media/video/video_decode_accelerator.h
+++ b/media/video/video_decode_accelerator.h
@@ -17,6 +17,7 @@
 #include "media/base/bitstream_buffer.h"
 #include "media/base/cdm_context.h"
 #include "media/base/encryption_scheme.h"
+#include "media/base/overlay_info.h"
 #include "media/base/surface_manager.h"
 #include "media/base/video_decoder_config.h"
 #include "media/video/picture.h"
@@ -150,14 +151,9 @@
     // Whether the client supports deferred initialization.
     bool is_deferred_initialization_allowed = false;
 
-    // An optional graphics surface that the VDA should render to. For setting
-    // an output SurfaceView on Android. It's only valid when not equal to
-    // |kNoSurfaceID|.
-    // TODO(liberato): should this be Optional<> instead?
-    int surface_id = SurfaceManager::kNoSurfaceID;
-
-    // An optional routing token for AndroidOverlay.
-    base::Optional<base::UnguessableToken> overlay_routing_token;
+    // Optional overlay info available at startup, rather than waiting for the
+    // VDA to receive a callback.
+    OverlayInfo overlay_info;
 
     // Coded size of the video frame hint, subject to change.
     gfx::Size initial_expected_coded_size = gfx::Size(320, 240);
@@ -313,9 +309,7 @@
   // previously set surface in favor of an internally generated texture.
   // |routing_token| is an optional AndroidOverlay routing token.  At most one
   // should be non-empty.
-  virtual void SetSurface(
-      int32_t surface_id,
-      const base::Optional<base::UnguessableToken>& routing_token);
+  virtual void SetOverlayInfo(const OverlayInfo& overlay_info);
 
   // Destroys the decoder: all pending inputs are dropped immediately and the
   // component is freed.  This call may asynchornously free system resources,
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 3bfe976..3ee51c9 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -36,7 +36,7 @@
 
 use_v8_in_net = !is_ios && !is_proto_quic
 enable_built_in_dns = !is_ios && !is_proto_quic
-enable_net_mojo = !is_ios && !is_android && !is_proto_quic
+enable_net_mojo = !is_ios && !is_proto_quic
 
 # True if certificates are represented with DER byte buffers. This can be true
 # in addition to use_openssl_certs or use_nss_certs, in that case byte certs
@@ -2657,6 +2657,8 @@
       "proxy/proxy_resolver_factory_mojo.h",
       "proxy/proxy_service_mojo.cc",
       "proxy/proxy_service_mojo.h",
+      "url_request/url_request_context_builder_mojo.cc",
+      "url_request/url_request_context_builder_mojo.h",
     ]
 
     public_deps = [
@@ -2691,33 +2693,6 @@
   }
 }
 
-if (use_v8_in_net) {
-  source_set("net_context_builder_with_v8") {
-    sources = [
-      "url_request/url_request_context_builder_v8.cc",
-      "url_request/url_request_context_builder_v8.h",
-    ]
-
-    defines = []
-
-    deps = [
-      ":net",
-      ":net_with_v8",
-      "//base",
-    ]
-
-    if (enable_net_mojo) {
-      deps += [
-        ":net_browser_services",
-        "//mojo/public/cpp/bindings",
-        "//net/interfaces",
-      ]
-
-      defines += [ "ENABLE_NET_MOJO" ]
-    }
-  }
-}
-
 if (!is_ios && !is_android) {
   executable("cert_verify_tool") {
     testonly = true
@@ -5089,8 +5064,8 @@
     "url_request/sdch_dictionary_fetcher_unittest.cc",
     "url_request/url_fetcher_impl_unittest.cc",
     "url_request/url_fetcher_response_writer_unittest.cc",
+    "url_request/url_request_context_builder_mojo_unittest.cc",
     "url_request/url_request_context_builder_unittest.cc",
-    "url_request/url_request_context_builder_v8_unittest.cc",
     "url_request/url_request_context_unittest.cc",
     "url_request/url_request_data_job_unittest.cc",
     "url_request/url_request_file_dir_job_unittest.cc",
@@ -5329,16 +5304,12 @@
   }
 
   if (use_v8_in_net) {
-    deps += [
-      ":net_context_builder_with_v8",
-      ":net_with_v8",
-    ]
+    deps += [ ":net_with_v8" ]
   } else {
     sources -= [
       "proxy/proxy_resolver_v8_tracing_unittest.cc",
       "proxy/proxy_resolver_v8_tracing_wrapper_unittest.cc",
       "proxy/proxy_resolver_v8_unittest.cc",
-      "url_request/url_request_context_builder_v8_unittest.cc",
     ]
   }
 
@@ -5348,8 +5319,6 @@
       ":net_utility_services",
       "//mojo/edk/system",
     ]
-
-    defines += [ "ENABLE_NET_MOJO" ]
   } else {
     sources -= [
       "dns/host_resolver_mojo_unittest.cc",
@@ -5361,6 +5330,7 @@
       "proxy/proxy_service_mojo_unittest.cc",
       "proxy/test_mojo_proxy_resolver_factory.cc",
       "proxy/test_mojo_proxy_resolver_factory.h",
+      "url_request/url_request_context_builder_mojo_unittest.cc",
     ]
   }
 
diff --git a/net/test/run_all_unittests.cc b/net/test/run_all_unittests.cc
index 78307f7..becfa33 100644
--- a/net/test/run_all_unittests.cc
+++ b/net/test/run_all_unittests.cc
@@ -18,7 +18,7 @@
 #include "net/android/net_jni_registrar.h"
 #endif
 
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#if !defined(OS_IOS)
 #include "mojo/edk/embedder/embedder.h"  // nogncheck
 #endif
 
@@ -81,7 +81,7 @@
   NetTestSuite test_suite(argc, argv);
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
 
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#if !defined(OS_IOS)
   mojo::edk::Init();
 #endif
 
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index da93630..30558b6 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -208,6 +208,8 @@
       sdch_enabled_(false),
       cookie_store_set_by_client_(false),
       net_log_(nullptr),
+      pac_quick_check_enabled_(true),
+      pac_sanitize_url_policy_(ProxyService::SanitizeUrlPolicy::SAFE),
       socket_performance_watcher_factory_(nullptr) {
 }
 
@@ -407,6 +409,8 @@
         CreateProxyService(std::move(proxy_config_service_), context.get(),
                            context->host_resolver(),
                            context->network_delegate(), context->net_log());
+    proxy_service_->set_quick_check_enabled(pac_quick_check_enabled_);
+    proxy_service_->set_sanitize_url_policy(pac_sanitize_url_policy_);
   }
   storage->set_proxy_service(std::move(proxy_service_));
 
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index 90ed04c..a67f6aa8 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -116,6 +116,20 @@
     proxy_config_service_ = std::move(proxy_config_service);
   }
 
+  // Sets whether quick PAC checks are enabled. Defaults to true. Ignored if
+  // a ProxyService is set directly.
+  void set_pac_quick_check_enabled(bool pac_quick_check_enabled) {
+    pac_quick_check_enabled_ = pac_quick_check_enabled;
+  }
+
+  // Sets policy for sanitizing URLs before passing them to a PAC. Defaults to
+  // ProxyService::SanitizeUrlPolicy::SAFE. Ignored if
+  // a ProxyService is set directly.
+  void set_pac_sanitize_url_policy(
+      net::ProxyService::SanitizeUrlPolicy pac_sanitize_url_policy) {
+    pac_sanitize_url_policy_ = pac_sanitize_url_policy;
+  }
+
   // Sets the proxy service. If one is not provided, by default, uses system
   // libraries to evaluate PAC scripts, if available (And if not, skips PAC
   // resolution). Subclasses may override CreateProxyService for different
@@ -349,6 +363,8 @@
   std::unique_ptr<HostResolver> host_resolver_;
   std::unique_ptr<ChannelIDService> channel_id_service_;
   std::unique_ptr<ProxyConfigService> proxy_config_service_;
+  bool pac_quick_check_enabled_;
+  ProxyService::SanitizeUrlPolicy pac_sanitize_url_policy_;
   std::unique_ptr<ProxyService> proxy_service_;
   std::unique_ptr<NetworkDelegate> network_delegate_;
   std::unique_ptr<ProxyDelegate> proxy_delegate_;
diff --git a/net/url_request/url_request_context_builder_mojo.cc b/net/url_request/url_request_context_builder_mojo.cc
new file mode 100644
index 0000000..43a4fc2
--- /dev/null
+++ b/net/url_request/url_request_context_builder_mojo.cc
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/url_request/url_request_context_builder_mojo.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "net/proxy/proxy_config_service.h"
+#include "net/proxy/proxy_script_fetcher_impl.h"
+#include "net/proxy/proxy_service_mojo.h"
+
+namespace net {
+
+URLRequestContextBuilderMojo::URLRequestContextBuilderMojo()
+    : dhcp_fetcher_factory_(new DhcpProxyScriptFetcherFactory()) {}
+
+URLRequestContextBuilderMojo::~URLRequestContextBuilderMojo() = default;
+
+std::unique_ptr<ProxyService> URLRequestContextBuilderMojo::CreateProxyService(
+    std::unique_ptr<ProxyConfigService> proxy_config_service,
+    URLRequestContext* url_request_context,
+    HostResolver* host_resolver,
+    NetworkDelegate* network_delegate,
+    NetLog* net_log) {
+  DCHECK(url_request_context);
+  DCHECK(host_resolver);
+
+  if (!mojo_proxy_resolver_factory_) {
+    return URLRequestContextBuilder::CreateProxyService(
+        std::move(proxy_config_service), url_request_context, host_resolver,
+        network_delegate, net_log);
+  }
+
+  std::unique_ptr<net::DhcpProxyScriptFetcher> dhcp_proxy_script_fetcher =
+      dhcp_fetcher_factory_->Create(url_request_context);
+  std::unique_ptr<net::ProxyScriptFetcher> proxy_script_fetcher =
+      base::MakeUnique<ProxyScriptFetcherImpl>(url_request_context);
+  return CreateProxyServiceUsingMojoFactory(
+      mojo_proxy_resolver_factory_, std::move(proxy_config_service),
+      proxy_script_fetcher.release(), std::move(dhcp_proxy_script_fetcher),
+      host_resolver, net_log, network_delegate);
+}
+
+}  // namespace net
diff --git a/net/url_request/url_request_context_builder_mojo.h b/net/url_request/url_request_context_builder_mojo.h
new file mode 100644
index 0000000..0610864c
--- /dev/null
+++ b/net/url_request/url_request_context_builder_mojo.h
@@ -0,0 +1,65 @@
+// 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 NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_MOJO_H_
+#define NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_MOJO_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace net {
+
+class HostResolver;
+class NetLog;
+class NetworkDelegate;
+class MojoProxyResolverFactory;
+class ProxyService;
+class URLRequestContext;
+
+// Specialization of URLRequestContextBuilder that can create a ProxyService
+// that uses a Mojo ProxyResolver. The consumer is responsible for providing
+// the MojoProxyResolverFactory.  If a PoxyService is set directly via the
+// URLRequestContextBuilder API, it will be used instead.
+class URLRequestContextBuilderMojo : public URLRequestContextBuilder {
+ public:
+  URLRequestContextBuilderMojo();
+  ~URLRequestContextBuilderMojo() override;
+
+  // Overrides default DhcpProxyScriptFetcherFactory. Ignored if no
+  // MojoProxyResolverFactory is provided.
+  void set_dhcp_fetcher_factory(
+      std::unique_ptr<DhcpProxyScriptFetcherFactory> dhcp_fetcher_factory) {
+    dhcp_fetcher_factory = std::move(dhcp_fetcher_factory_);
+  }
+
+  // Sets Mojo factory used to create ProxyResolvers. If not set, falls back to
+  // URLRequestContext's default behavior. The passed in factory must outlive
+  // the URLRequestContext the builder creates.
+  void set_mojo_proxy_resolver_factory(
+      MojoProxyResolverFactory* mojo_proxy_resolver_factory) {
+    mojo_proxy_resolver_factory_ = mojo_proxy_resolver_factory;
+  }
+
+ private:
+  std::unique_ptr<ProxyService> CreateProxyService(
+      std::unique_ptr<ProxyConfigService> proxy_config_service,
+      URLRequestContext* url_request_context,
+      HostResolver* host_resolver,
+      NetworkDelegate* network_delegate,
+      NetLog* net_log) override;
+
+  std::unique_ptr<DhcpProxyScriptFetcherFactory> dhcp_fetcher_factory_;
+
+  MojoProxyResolverFactory* mojo_proxy_resolver_factory_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestContextBuilderMojo);
+};
+
+}  // namespace net
+
+#endif  // NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_MOJO_H_
diff --git a/net/url_request/url_request_context_builder_v8_unittest.cc b/net/url_request/url_request_context_builder_mojo_unittest.cc
similarity index 76%
rename from net/url_request/url_request_context_builder_v8_unittest.cc
rename to net/url_request/url_request_context_builder_mojo_unittest.cc
index f302d35..f413db7 100644
--- a/net/url_request/url_request_context_builder_v8_unittest.cc
+++ b/net/url_request/url_request_context_builder_mojo_unittest.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/url_request/url_request_context_builder_v8.h"
+#include "net/url_request/url_request_context_builder_mojo.h"
 
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/host_port_pair.h"
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_config_service_fixed.h"
+#include "net/proxy/test_mojo_proxy_resolver_factory.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -21,10 +22,6 @@
 #include "testing/platform_test.h"
 #include "url/gurl.h"
 
-#ifdef ENABLE_NET_MOJO
-#include "net/proxy/test_mojo_proxy_resolver_factory.h"
-#endif
-
 namespace net {
 
 namespace {
@@ -46,63 +43,19 @@
   return std::move(response);
 }
 
-class URLRequestContextBuilderV8Test : public PlatformTest {
+class URLRequestContextBuilderMojoTest : public PlatformTest {
  protected:
-  URLRequestContextBuilderV8Test() {
+  URLRequestContextBuilderMojoTest() {
     test_server_.RegisterRequestHandler(base::Bind(&HandlePacRequest));
     test_server_.AddDefaultHandlers(
         base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
   }
 
   EmbeddedTestServer test_server_;
-  URLRequestContextBuilderV8 builder_;
+  URLRequestContextBuilderMojo builder_;
 };
 
-TEST_F(URLRequestContextBuilderV8Test, V8InProcess) {
-  EXPECT_TRUE(test_server_.Start());
-
-  builder_.set_proxy_config_service(base::MakeUnique<ProxyConfigServiceFixed>(
-      ProxyConfig::CreateFromCustomPacURL(test_server_.GetURL(kPacPath))));
-  std::unique_ptr<URLRequestContext> context(builder_.Build());
-
-  TestDelegate delegate;
-  std::unique_ptr<URLRequest> request(context->CreateRequest(
-      GURL("http://hats:12345/echoheader?Foo"), DEFAULT_PRIORITY, &delegate,
-      TRAFFIC_ANNOTATION_FOR_TESTS));
-  request->SetExtraRequestHeaderByName("Foo", "Bar", false);
-  request->Start();
-  base::RunLoop().Run();
-  EXPECT_EQ("Bar", delegate.data_received());
-}
-
-// Makes sure that pending PAC requests are correctly shutdown during teardown.
-TEST_F(URLRequestContextBuilderV8Test, V8InProcessShutdownWithHungRequest) {
-  test_server::SimpleConnectionListener connection_listener(
-      1, test_server::SimpleConnectionListener::FAIL_ON_ADDITIONAL_CONNECTIONS);
-  test_server_.SetConnectionListener(&connection_listener);
-  EXPECT_TRUE(test_server_.Start());
-
-  builder_.set_proxy_config_service(base::MakeUnique<ProxyConfigServiceFixed>(
-      ProxyConfig::CreateFromCustomPacURL(test_server_.GetURL("/hung"))));
-
-  std::unique_ptr<URLRequestContext> context(builder_.Build());
-  TestDelegate delegate;
-  std::unique_ptr<URLRequest> request(context->CreateRequest(
-      GURL("http://hats:12345/echoheader?Foo"), DEFAULT_PRIORITY, &delegate,
-      TRAFFIC_ANNOTATION_FOR_TESTS));
-  request->Start();
-  connection_listener.WaitForConnections();
-
-  // Have to shut down the test server before |connection_listener| falls out of
-  // scope.
-  EXPECT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
-
-  // Tearing down the URLRequestContext should not cause an AssertNoURLRequests
-  // failure.
-}
-
-#ifdef ENABLE_NET_MOJO
-TEST_F(URLRequestContextBuilderV8Test, MojoProxyResolver) {
+TEST_F(URLRequestContextBuilderMojoTest, MojoProxyResolver) {
   EXPECT_TRUE(test_server_.Start());
   TestMojoProxyResolverFactory::GetInstance()->set_resolver_created(false);
 
@@ -124,7 +77,36 @@
   // Make sure that the Mojo factory was used.
   EXPECT_TRUE(TestMojoProxyResolverFactory::GetInstance()->resolver_created());
 }
-#endif  // ENABLE_NET_MOJO
+
+// Makes sure that pending PAC requests are correctly shut down during teardown.
+TEST_F(URLRequestContextBuilderMojoTest, ShutdownWithHungRequest) {
+  test_server::SimpleConnectionListener connection_listener(
+      1, test_server::SimpleConnectionListener::FAIL_ON_ADDITIONAL_CONNECTIONS);
+  test_server_.SetConnectionListener(&connection_listener);
+  EXPECT_TRUE(test_server_.Start());
+
+  builder_.set_proxy_config_service(base::MakeUnique<ProxyConfigServiceFixed>(
+      ProxyConfig::CreateFromCustomPacURL(test_server_.GetURL("/hung"))));
+  builder_.set_mojo_proxy_resolver_factory(
+      TestMojoProxyResolverFactory::GetInstance());
+
+  std::unique_ptr<URLRequestContext> context(builder_.Build());
+  TestDelegate delegate;
+  std::unique_ptr<URLRequest> request(context->CreateRequest(
+      GURL("http://hats:12345/echoheader?Foo"), DEFAULT_PRIORITY, &delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS));
+  request->Start();
+  connection_listener.WaitForConnections();
+
+  // Tearing down the URLRequestContext should not cause an AssertNoURLRequests
+  // failure.
+  request.reset();
+  context.reset();
+
+  // Have to shut down the test server before |connection_listener| falls out of
+  // scope.
+  EXPECT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
+}
 
 }  // namespace
 
diff --git a/net/url_request/url_request_context_builder_v8.cc b/net/url_request/url_request_context_builder_v8.cc
deleted file mode 100644
index d16f4d7..0000000
--- a/net/url_request/url_request_context_builder_v8.cc
+++ /dev/null
@@ -1,64 +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 "net/url_request/url_request_context_builder_v8.h"
-
-#include "base/logging.h"
-#include "net/proxy/proxy_config_service.h"
-#include "net/proxy/proxy_script_fetcher_impl.h"
-#include "net/proxy/proxy_service_v8.h"
-
-#ifdef ENABLE_NET_MOJO
-#include "net/proxy/proxy_service_mojo.h"
-#endif
-
-namespace net {
-
-URLRequestContextBuilderV8::URLRequestContextBuilderV8()
-    : dhcp_fetcher_factory_(new DhcpProxyScriptFetcherFactory()) {}
-
-URLRequestContextBuilderV8::~URLRequestContextBuilderV8() = default;
-
-std::unique_ptr<ProxyService> URLRequestContextBuilderV8::CreateProxyService(
-    std::unique_ptr<ProxyConfigService> proxy_config_service,
-    URLRequestContext* url_request_context,
-    HostResolver* host_resolver,
-    NetworkDelegate* network_delegate,
-    NetLog* net_log) {
-  DCHECK(url_request_context);
-  DCHECK(host_resolver);
-
-  if (!use_v8_) {
-    return URLRequestContextBuilder::CreateProxyService(
-        std::move(proxy_config_service), url_request_context, host_resolver,
-        network_delegate, net_log);
-  }
-
-  std::unique_ptr<net::DhcpProxyScriptFetcher> dhcp_proxy_script_fetcher =
-      dhcp_fetcher_factory_->Create(url_request_context);
-  std::unique_ptr<net::ProxyScriptFetcher> proxy_script_fetcher =
-      base::MakeUnique<ProxyScriptFetcherImpl>(url_request_context);
-  std::unique_ptr<ProxyService> proxy_service;
-#ifdef ENABLE_NET_MOJO
-  if (mojo_proxy_resolver_factory_) {
-    proxy_service = CreateProxyServiceUsingMojoFactory(
-        mojo_proxy_resolver_factory_, std::move(proxy_config_service),
-        proxy_script_fetcher.release(), std::move(dhcp_proxy_script_fetcher),
-        host_resolver, net_log, network_delegate);
-  }
-#endif  // ENABLE_NET_MOJO
-  if (!proxy_service) {
-    proxy_service = CreateProxyServiceUsingV8ProxyResolver(
-        std::move(proxy_config_service), proxy_script_fetcher.release(),
-        std::move(dhcp_proxy_script_fetcher), host_resolver, net_log,
-        network_delegate);
-  }
-
-  proxy_service->set_quick_check_enabled(quick_check_enabled_);
-  proxy_service->set_sanitize_url_policy(sanitize_url_policy_);
-
-  return proxy_service;
-}
-
-}  // namespace net
diff --git a/net/url_request/url_request_context_builder_v8.h b/net/url_request/url_request_context_builder_v8.h
deleted file mode 100644
index e44c359..0000000
--- a/net/url_request/url_request_context_builder_v8.h
+++ /dev/null
@@ -1,92 +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 NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_V8_H_
-#define NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_V8_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
-#include "net/proxy/proxy_service.h"
-#include "net/url_request/url_request_context_builder.h"
-
-namespace net {
-
-class HostResolver;
-class NetLog;
-class NetworkDelegate;
-class MojoProxyResolverFactory;
-class URLRequestContext;
-
-// Specialization of URLRequestContextBuilder that can create a ProxyService
-// that uses a V8 ProxyResolver. PAC scripts are run by V8 in process, by
-// default, but a Mojo factory can be passed in for out-of-process resolution.
-// PAC scripts will be fetched using the request context itself. If a
-// PoxyService is set directly via the URLRequestContextBuilder API, it will be
-// used instead of the one this class normally creates.
-class URLRequestContextBuilderV8 : public URLRequestContextBuilder {
- public:
-  URLRequestContextBuilderV8();
-  ~URLRequestContextBuilderV8() override;
-
-  // If set to false, the URLrequestContextBuilder will create a ProxyService,
-  // which won't use V8. Defaults to true.
-  void set_use_v8(bool use_v8) { use_v8_ = use_v8; }
-
-  // Sets whether quick PAC checks are enabled. Defaults to true. Ignored if
-  // use_v8 is false.
-  void set_quick_check_enabled(bool quick_check_enabled) {
-    quick_check_enabled_ = quick_check_enabled;
-  }
-
-  // Sets policy for sanitizing URLs before passing them a PAC. Defaults to
-  // ProxyService::SanitizeUrlPolicy::SAFE. Ignored if use_v8 is false.
-  void set_pac_sanitize_url_policy(
-      net::ProxyService::SanitizeUrlPolicy sanitize_url_policy) {
-    sanitize_url_policy_ = sanitize_url_policy;
-  }
-
-  // Overrides default DhcpProxyScriptFetcherFactory. Ignored if use_v8 is
-  // false.
-  void set_dhcp_fetcher_factory(
-      std::unique_ptr<DhcpProxyScriptFetcherFactory> dhcp_fetcher_factory) {
-    dhcp_fetcher_factory = std::move(dhcp_fetcher_factory_);
-  }
-
-#ifdef ENABLE_NET_MOJO
-  // Sets Mojo factory used to create ProxyResolvers. If not set, V8 will be
-  // used in process instead of Mojo. Ignored if use_v8 is false. The passed in
-  // factory must outlive the URLRequestContext the builder creates.
-  void set_mojo_proxy_resolver_factory(
-      MojoProxyResolverFactory* mojo_proxy_resolver_factory) {
-    mojo_proxy_resolver_factory_ = mojo_proxy_resolver_factory;
-  }
-#endif  // ENABLE_NET_MOJO
-
- private:
-  std::unique_ptr<ProxyService> CreateProxyService(
-      std::unique_ptr<ProxyConfigService> proxy_config_service,
-      URLRequestContext* url_request_context,
-      HostResolver* host_resolver,
-      NetworkDelegate* network_delegate,
-      NetLog* net_log) override;
-
-  bool use_v8_ = true;
-  bool quick_check_enabled_ = true;
-  net::ProxyService::SanitizeUrlPolicy sanitize_url_policy_ =
-      net::ProxyService::SanitizeUrlPolicy::SAFE;
-
-  std::unique_ptr<DhcpProxyScriptFetcherFactory> dhcp_fetcher_factory_;
-
-#ifdef ENABLE_NET_MOJO
-  MojoProxyResolverFactory* mojo_proxy_resolver_factory_ = nullptr;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(URLRequestContextBuilderV8);
-};
-
-}  // namespace net
-
-#endif  // NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_V8_H_
diff --git a/printing/backend/cups_jobs.cc b/printing/backend/cups_jobs.cc
index e73ae8fb..80daed7 100644
--- a/printing/backend/cups_jobs.cc
+++ b/printing/backend/cups_jobs.cc
@@ -11,8 +11,12 @@
 #include <memory>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/version.h"
+#include "printing/backend/cups_deleters.h"
 
 namespace printing {
 namespace {
@@ -26,6 +30,11 @@
 const char kPrinterStateReasons[] = "printer-state-reasons";
 const char kPrinterStateMessage[] = "printer-state-message";
 
+const char kPrinterMakeAndModel[] = "printer-make-and-model";
+const char kIppVersionsSupported[] = "ipp-versions-supported";
+const char kIppFeaturesSupported[] = "ipp-features-supported";
+const char kDocumentFormatSupported[] = "document-format-supported";
+
 // job attributes
 const char kJobUri[] = "job-uri";
 const char kJobId[] = "job-id";
@@ -44,6 +53,9 @@
 const char kCompleted[] = "completed";
 const char kNotCompleted[] = "not-completed";
 
+// ipp features
+const char kIppEverywhere[] = "ipp-everywhere";
+
 // printer state severities
 const char kSeverityReport[] = "report";
 const char kSeverityWarn[] = "warning";
@@ -88,10 +100,18 @@
 constexpr std::array<const char* const, 3> kPrinterAttributes{
     {kPrinterState, kPrinterStateReasons, kPrinterStateMessage}};
 
-std::unique_ptr<ipp_t, void (*)(ipp_t*)> WrapIpp(ipp_t* ipp) {
-  return std::unique_ptr<ipp_t, void (*)(ipp_t*)>(ipp, &ippDelete);
+constexpr std::array<const char* const, 4> kPrinterInfo{
+    {kPrinterMakeAndModel, kIppVersionsSupported, kIppFeaturesSupported,
+     kDocumentFormatSupported}};
+
+using ScopedIppPtr = std::unique_ptr<ipp_t, void (*)(ipp_t*)>;
+
+ScopedIppPtr WrapIpp(ipp_t* ipp) {
+  return ScopedIppPtr(ipp, &ippDelete);
 }
 
+using ScopedHttpPtr = std::unique_ptr<http_t, HttpDeleter>;
+
 // Converts an IPP attribute |attr| to the appropriate JobState enum.
 CupsJob::JobState ToJobState(ipp_attribute_t* attr) {
   DCHECK_EQ(IPP_TAG_ENUM, ippGetValueTag(attr));
@@ -281,6 +301,40 @@
   return base::StringPrintf("ipp://localhost/printers/%s", id.c_str());
 }
 
+// Extracts PrinterInfo fields from |response| and populates |printer_info|.
+// Returns true if at least printer-make-and-model and ipp-versions-supported
+// were read.
+bool ParsePrinterInfo(ipp_t* response, PrinterInfo* printer_info) {
+  for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
+       attr = ippNextAttribute(response)) {
+    base::StringPiece name = ippGetName(attr);
+
+    if (name == base::StringPiece(kPrinterMakeAndModel)) {
+      DCHECK_EQ(IPP_TAG_TEXT, ippGetValueTag(attr));
+      printer_info->make_and_model = ippGetString(attr, 0, nullptr);
+    } else if (name == base::StringPiece(kIppVersionsSupported)) {
+      std::vector<std::string> ipp_versions;
+      ParseCollection(attr, &ipp_versions);
+      for (const std::string& version : ipp_versions) {
+        base::Version major_minor(version);
+        if (major_minor.IsValid()) {
+          printer_info->ipp_versions.push_back(major_minor);
+        }
+      }
+    } else if (name == base::StringPiece(kIppFeaturesSupported)) {
+      std::vector<std::string> features;
+      ParseCollection(attr, &features);
+      printer_info->ipp_everywhere =
+          base::ContainsValue(features, kIppEverywhere);
+    } else if (name == base::StringPiece(kDocumentFormatSupported)) {
+      ParseCollection(attr, &printer_info->document_formats);
+    }
+  }
+
+  return !printer_info->make_and_model.empty() &&
+         !printer_info->ipp_versions.empty();
+}
+
 }  // namespace
 
 CupsJob::CupsJob() = default;
@@ -295,6 +349,10 @@
 
 PrinterStatus::~PrinterStatus() = default;
 
+PrinterInfo::PrinterInfo() = default;
+
+PrinterInfo::~PrinterInfo() = default;
+
 void ParseJobsResponse(ipp_t* response,
                        const std::string& printer_id,
                        std::vector<CupsJob>* jobs) {
@@ -309,6 +367,40 @@
   }
 }
 
+// Returns an IPP response for a Get-Printer-Attributes request to |http|.  For
+// print servers, |printer_uri| is used as the printer-uri value.
+// |resource_path| specifies the path portion of the server URI.
+// |num_attributes| is the number of attributes in |attributes| which should be
+// a list of IPP attributes.  |status| is updated with status code for the
+// request.  A successful request will have the |status| IPP_STATUS_OK.
+ScopedIppPtr GetPrinterAttributes(http_t* http,
+                                  const std::string& printer_uri,
+                                  const std::string& resource_path,
+                                  int num_attributes,
+                                  const char* const* attributes,
+                                  ipp_status_t* status) {
+  base::ThreadRestrictions::AssertIOAllowed();
+  DCHECK(http);
+
+  // CUPS expects a leading slash for resource names.  Add one if it's missing.
+  std::string rp = !resource_path.empty() && resource_path.front() == '/'
+                       ? resource_path
+                       : "/" + resource_path;
+
+  auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
+
+  ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
+               nullptr, printer_uri.data());
+
+  ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                kRequestedAttributes, num_attributes, nullptr, attributes);
+
+  auto response = WrapIpp(cupsDoRequest(http, request.release(), rp.c_str()));
+  *status = ippGetStatusCode(response.get());
+
+  return response;
+}
+
 void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status) {
   for (ipp_attribute_t* attr = ippFirstAttribute(response); attr != nullptr;
        attr = ippNextAttribute(response)) {
@@ -332,25 +424,45 @@
   }
 }
 
+bool GetPrinterInfo(const std::string& address,
+                    const int port,
+                    const std::string& resource,
+                    PrinterInfo* printer_info) {
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  ScopedHttpPtr http = ScopedHttpPtr(
+      httpConnect2(address.data(), port, nullptr, AF_INET,
+                   HTTP_ENCRYPTION_IF_REQUESTED, 0, 200, nullptr));
+  if (!http) {
+    LOG(WARNING) << "Could not connect to host";
+    return false;
+  }
+
+  ipp_status_t status;
+  ScopedIppPtr response =
+      GetPrinterAttributes(http.get(), resource, resource, kPrinterInfo.size(),
+                           kPrinterInfo.data(), &status);
+  if (status != IPP_STATUS_OK || response.get() == nullptr) {
+    LOG(WARNING) << "Get attributes failure: " << status;
+    return false;
+  }
+
+  return ParsePrinterInfo(response.get(), printer_info);
+}
+
 bool GetPrinterStatus(http_t* http,
                       const std::string& printer_id,
                       PrinterStatus* printer_status) {
-  DCHECK(http);
+  base::ThreadRestrictions::AssertIOAllowed();
 
-  auto request = WrapIpp(ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES));
-
+  ipp_status_t status;
   const std::string printer_uri = PrinterUriFromName(printer_id);
-  ippAddString(request.get(), IPP_TAG_OPERATION, IPP_TAG_URI, kPrinterUri,
-               nullptr, printer_uri.data());
 
-  ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                kRequestedAttributes, kPrinterAttributes.size(), nullptr,
-                kPrinterAttributes.data());
+  ScopedIppPtr response =
+      GetPrinterAttributes(http, printer_uri, "/", kPrinterAttributes.size(),
+                           kPrinterAttributes.data(), &status);
 
-  auto response =
-      WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str()));
-
-  if (ippGetStatusCode(response.get()) != IPP_STATUS_OK)
+  if (status != IPP_STATUS_OK)
     return false;
 
   ParsePrinterStatus(response.get(), printer_status);
@@ -363,6 +475,7 @@
                  int limit,
                  JobCompletionState which,
                  std::vector<CupsJob>* jobs) {
+  base::ThreadRestrictions::AssertIOAllowed();
   DCHECK(http);
 
   auto request = WrapIpp(ippNewRequest(IPP_OP_GET_JOBS));
@@ -390,12 +503,11 @@
   }
 
   // cupsDoRequest will delete the request.
-  auto response =
-      WrapIpp(cupsDoRequest(http, request.release(), printer_uri.c_str()));
+  auto response = WrapIpp(cupsDoRequest(http, request.release(), "/"));
 
   ipp_status_t status = ippGetStatusCode(response.get());
 
-  if (status != IPP_OK) {
+  if (status != IPP_STATUS_OK) {
     LOG(WARNING) << "IPP Error: " << cupsLastErrorString();
     return false;
   }
diff --git a/printing/backend/cups_jobs.h b/printing/backend/cups_jobs.h
index b1ca693c..8c0e4d4b 100644
--- a/printing/backend/cups_jobs.h
+++ b/printing/backend/cups_jobs.h
@@ -10,10 +10,16 @@
 #include <cups/cups.h>
 
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "base/version.h"
 #include "printing/printing_export.h"
 
+// This file contains a collection of functions used to query IPP printers or
+// print servers and the related code to parse these responses.  All Get*
+// operations block on the network request and cannot be run on the UI thread.
+
 namespace printing {
 
 // Represents a print job sent to the queue.
@@ -110,6 +116,27 @@
   std::string message;
 };
 
+struct PRINTING_EXPORT PrinterInfo {
+  PrinterInfo();
+  PrinterInfo(const PrinterInfo& info);
+
+  ~PrinterInfo();
+
+  // printer-make-and-model
+  std::string make_and_model;
+
+  // document-format-supported
+  // MIME types for supported formats.
+  std::vector<std::string> document_formats;
+
+  // ipp-versions-supported
+  // A collection of supported IPP protocol versions.
+  std::vector<base::Version> ipp_versions;
+
+  // Does ipp-features-supported contain 'ipp-everywhere'.
+  bool ipp_everywhere = false;
+};
+
 // Specifies classes of jobs.
 enum JobCompletionState {
   COMPLETED,  // only completed jobs
@@ -125,6 +152,13 @@
 // Attempts to extract a PrinterStatus object out of |response|.
 void ParsePrinterStatus(ipp_t* response, PrinterStatus* printer_status);
 
+// Queries the printer at |address| on |port| with a Get-Printer-Attributes
+// request to populate |printer_info|.  Returns false if the request failed.
+bool PRINTING_EXPORT GetPrinterInfo(const std::string& address,
+                                    const int port,
+                                    const std::string& resource,
+                                    PrinterInfo* printer_info);
+
 // Attempts to retrieve printer status using connection |http| for |printer_id|.
 // Returns true if succcssful and updates the fields in |printer_status| as
 // appropriate.  Returns false if the request failed.
diff --git a/remoting/host/it2me/it2me_host.cc b/remoting/host/it2me/it2me_host.cc
index d70b26b..9575eda 100644
--- a/remoting/host/it2me/it2me_host.cc
+++ b/remoting/host/it2me/it2me_host.cc
@@ -69,7 +69,8 @@
     base::WeakPtr<It2MeHost::Observer> observer,
     std::unique_ptr<SignalStrategy> signal_strategy,
     const std::string& username,
-    const std::string& directory_bot_jid) {
+    const std::string& directory_bot_jid,
+    const protocol::IceConfig& ice_config) {
   DCHECK(host_context->ui_task_runner()->BelongsToCurrentThread());
 
   host_context_ = std::move(host_context);
@@ -87,7 +88,7 @@
   // Switch to the network thread to start the actual connection.
   host_context_->network_task_runner()->PostTask(
       FROM_HERE, base::Bind(&It2MeHost::ConnectOnNetworkThread, this, username,
-                            directory_bot_jid));
+                            directory_bot_jid, ice_config));
 }
 
 void It2MeHost::Disconnect() {
@@ -97,7 +98,8 @@
 }
 
 void It2MeHost::ConnectOnNetworkThread(const std::string& username,
-                                       const std::string& directory_bot_jid) {
+                                       const std::string& directory_bot_jid,
+                                       const protocol::IceConfig& ice_config) {
   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
   DCHECK_EQ(kDisconnected, state_);
 
@@ -158,8 +160,7 @@
           base::WrapUnique(new ChromiumUrlRequestFactory(
               host_context_->url_request_context_getter())),
           network_settings, protocol::TransportRole::SERVER);
-  transport_context->set_ice_config_url(
-      ServiceUrls::GetInstance()->ice_config_url());
+  transport_context->set_turn_ice_config(ice_config);
 
   std::unique_ptr<protocol::SessionManager> session_manager(
       new protocol::JingleSessionManager(signal_strategy_.get()));
diff --git a/remoting/host/it2me/it2me_host.h b/remoting/host/it2me/it2me_host.h
index 1ee72f30d..ca39912 100644
--- a/remoting/host/it2me/it2me_host.h
+++ b/remoting/host/it2me/it2me_host.h
@@ -22,7 +22,7 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
 namespace remoting {
 
@@ -34,6 +34,10 @@
 class RegisterSupportHostRequest;
 class RsaKeyPair;
 
+namespace protocol {
+struct IceConfig;
+}  // namespace protocol
+
 // These state values are duplicated in host_session.js.  Remember to update
 // both copies when making changes.
 enum It2MeHostState {
@@ -73,7 +77,8 @@
       base::WeakPtr<It2MeHost::Observer> observer,
       std::unique_ptr<SignalStrategy> signal_strategy,
       const std::string& username,
-      const std::string& directory_bot_jid);
+      const std::string& directory_bot_jid,
+      const protocol::IceConfig& ice_config);
 
   // Disconnects and shuts down the host.
   virtual void Disconnect();
@@ -106,8 +111,7 @@
 
  private:
   friend class MockIt2MeHost;
-  FRIEND_TEST_ALL_PREFIXES(It2MeHostTest, HostUdpPortRangePolicy_ValidRange);
-  FRIEND_TEST_ALL_PREFIXES(It2MeHostTest, HostUdpPortRangePolicy_NoRange);
+  friend class It2MeHostTest;
 
   // Updates state of the host. Can be called only on the network thread.
   void SetState(It2MeHostState state, const std::string& error_message);
@@ -122,7 +126,8 @@
 
   // Task posted to the network thread from Connect().
   void ConnectOnNetworkThread(const std::string& username,
-                              const std::string& directory_bot_jid);
+                              const std::string& directory_bot_jid,
+                              const protocol::IceConfig& ice_config);
 
   // Called when the support host registration completes.
   void OnReceivedSupportID(const std::string& support_id,
diff --git a/remoting/host/it2me/it2me_host_unittest.cc b/remoting/host/it2me/it2me_host_unittest.cc
index 10cec61..8f8e7ef 100644
--- a/remoting/host/it2me/it2me_host_unittest.cc
+++ b/remoting/host/it2me/it2me_host_unittest.cc
@@ -54,6 +54,8 @@
 // Note that this is intentionally different from the default port range.
 const char kPortRange[] = "12401-12408";
 
+const char kTestStunServer[] = "test_relay_server.com";
+
 }  // namespace
 
 class FakeIt2MeConfirmationDialog : public It2MeConfirmationDialog {
@@ -160,6 +162,8 @@
   static base::ListValue MakeList(
       std::initializer_list<base::StringPiece> values);
 
+  ChromotingHost* GetHost() { return it2me_host_->host_.get(); }
+
   ValidationResult validation_result_ = ValidationResult::SUCCESS;
 
   base::Closure state_change_callback_;
@@ -259,13 +263,18 @@
       new FakeIt2MeDialogFactory());
   dialog_factory_ = dialog_factory.get();
 
+  protocol::IceConfig ice_config;
+  ice_config.stun_servers.push_back(rtc::SocketAddress(kTestStunServer, 100));
+  ice_config.expiration_time =
+      base::Time::Now() + base::TimeDelta::FromHours(1);
+
   it2me_host_ = new It2MeHost();
   it2me_host_->Connect(host_context_->Copy(),
                        base::MakeUnique<base::DictionaryValue>(*policies_),
                        std::move(dialog_factory), weak_factory_.GetWeakPtr(),
                        base::WrapUnique(new FakeSignalStrategy(
                            SignalingAddress("fake_local_jid"))),
-                       kTestUserName, "fake_bot_jid");
+                       kTestUserName, "fake_bot_jid", ice_config);
 
   base::RunLoop run_loop;
   state_change_callback_ =
@@ -330,9 +339,32 @@
   return result;
 }
 
-TEST_F(It2MeHostTest, HostValidation_NoHostDomainListPolicy) {
+// Callback to receive IceConfig from TransportContext
+void ReceiveIceConfig(protocol::IceConfig* ice_config,
+                      const protocol::IceConfig& received_ice_config) {
+  *ice_config = received_ice_config;
+}
+
+TEST_F(It2MeHostTest, StartAndStop) {
   StartHost();
   ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
+
+  ShutdownHost();
+  ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
+}
+
+// Verify that IceConfig is passed to the TransportContext.
+TEST_F(It2MeHostTest, IceConfig) {
+  StartHost();
+  ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
+
+  protocol::IceConfig ice_config;
+  GetHost()->transport_context_for_tests()->set_relay_mode(
+      protocol::TransportContext::TURN);
+  GetHost()->transport_context_for_tests()->GetIceConfig(
+      base::Bind(&ReceiveIceConfig, &ice_config));
+  EXPECT_EQ(ice_config.stun_servers[0].hostname(), kTestStunServer);
+
   ShutdownHost();
   ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
 }
@@ -522,18 +554,16 @@
   SetPolicies(
       {{policy::key::kRemoteAccessHostUdpPortRange, base::Value(kPortRange)}});
   StartHost();
-  PortRange port_range = it2me_host_->host_->transport_context_for_tests()
-                             ->network_settings()
-                             .port_range;
+  PortRange port_range =
+      GetHost()->transport_context_for_tests()->network_settings().port_range;
   ASSERT_EQ(port_range_actual.min_port, port_range.min_port);
   ASSERT_EQ(port_range_actual.max_port, port_range.max_port);
 }
 
 TEST_F(It2MeHostTest, HostUdpPortRangePolicy_NoRange) {
   StartHost();
-  PortRange port_range = it2me_host_->host_->transport_context_for_tests()
-                             ->network_settings()
-                             .port_range;
+  PortRange port_range =
+      GetHost()->transport_context_for_tests()->network_settings().port_range;
   ASSERT_TRUE(port_range.is_null());
 }
 
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index bee792e0..6212deb1 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -33,6 +33,7 @@
 #include "remoting/host/host_exit_codes.h"
 #include "remoting/host/it2me/it2me_confirmation_dialog.h"
 #include "remoting/host/policy_watcher.h"
+#include "remoting/protocol/ice_config.h"
 #include "remoting/signaling/delegating_signal_strategy.h"
 
 #if defined(OS_WIN)
@@ -316,6 +317,12 @@
   }
 #endif  // !defined(NDEBUG)
 
+  base::DictionaryValue* ice_config_dict;
+  protocol::IceConfig ice_config;
+  if (message->GetDictionary("iceConfig", &ice_config_dict)) {
+    ice_config = protocol::IceConfig::Parse(*ice_config_dict);
+  }
+
   std::unique_ptr<base::DictionaryValue> policies =
       policy_watcher_->GetCurrentPolicies();
   if (policies->size() == 0) {
@@ -332,7 +339,7 @@
   it2me_host_->Connect(host_context_->Copy(), std::move(policies),
                        base::MakeUnique<It2MeConfirmationDialogFactory>(),
                        weak_ptr_, std::move(signal_strategy), username,
-                       directory_bot_jid);
+                       directory_bot_jid, ice_config);
 
   SendMessageToClient(std::move(response));
 }
diff --git a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
index 730b61b..962456c 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
@@ -33,6 +33,7 @@
 #include "remoting/host/native_messaging/pipe_messaging_channel.h"
 #include "remoting/host/policy_watcher.h"
 #include "remoting/host/setup/test_util.h"
+#include "remoting/protocol/ice_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace remoting {
@@ -43,6 +44,8 @@
 constexpr base::TimeDelta kTestAccessCodeLifetime =
     base::TimeDelta::FromSeconds(666);
 const char kTestClientUsername[] = "some_user@gmail.com";
+const char kTestBotJid[] = "remoting@bot.talk.google.com";
+const char kTestStunServer[] = "test_relay_server.com";
 
 void VerifyId(std::unique_ptr<base::DictionaryValue> response,
               int expected_value) {
@@ -91,7 +94,8 @@
                base::WeakPtr<It2MeHost::Observer> observer,
                std::unique_ptr<SignalStrategy> signal_strategy,
                const std::string& username,
-               const std::string& directory_bot_jid) override;
+               const std::string& directory_bot_jid,
+               const protocol::IceConfig& ice_config) override;
   void Disconnect() override;
 
  private:
@@ -109,9 +113,15 @@
     base::WeakPtr<It2MeHost::Observer> observer,
     std::unique_ptr<SignalStrategy> signal_strategy,
     const std::string& username,
-    const std::string& directory_bot_jid) {
+    const std::string& directory_bot_jid,
+    const protocol::IceConfig& ice_config) {
   DCHECK(context->ui_task_runner()->BelongsToCurrentThread());
 
+  // Verify that parameters are passed correctly.
+  EXPECT_EQ(username, kTestClientUsername);
+  EXPECT_EQ(directory_bot_jid, kTestBotJid);
+  EXPECT_EQ(ice_config.stun_servers[0].hostname(), kTestStunServer);
+
   host_context_ = std::move(context);
   observer_ = std::move(observer);
   signal_strategy_ = std::move(signal_strategy);
@@ -549,9 +559,14 @@
   connect_message.SetString("type", "connect");
   connect_message.SetString("xmppServerAddress", "talk.google.com:5222");
   connect_message.SetBoolean("xmppServerUseTls", true);
-  connect_message.SetString("directoryBotJid", "remoting@bot.talk.google.com");
-  connect_message.SetString("userName", "chromo.pyauto@gmail.com");
+  connect_message.SetString("directoryBotJid", kTestBotJid);
+  connect_message.SetString("userName", kTestClientUsername);
   connect_message.SetString("authServiceWithToken", "oauth2:sometoken");
+  connect_message.Set(
+      "iceConfig",
+      base::JSONReader::Read("{ \"iceServers\": [ { \"urls\": [ \"stun:" +
+                             std::string(kTestStunServer) + "\" ] } ] }"));
+
   WriteMessageToInputPipe(connect_message);
 }
 
diff --git a/remoting/protocol/ice_config.cc b/remoting/protocol/ice_config.cc
index 63268270..79c2f1a 100644
--- a/remoting/protocol/ice_config.cc
+++ b/remoting/protocol/ice_config.cc
@@ -5,6 +5,7 @@
 #include "remoting/protocol/ice_config.h"
 
 #include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
@@ -92,12 +93,9 @@
 IceConfig::~IceConfig() {}
 
 // static
-IceConfig IceConfig::Parse(const std::string& config_json) {
-  std::unique_ptr<base::Value> json = base::JSONReader::Read(config_json);
-  base::DictionaryValue* dictionary = nullptr;
-  base::ListValue* ice_servers_list = nullptr;
-  if (!json || !json->GetAsDictionary(&dictionary) ||
-      !dictionary->GetList("iceServers", &ice_servers_list)) {
+IceConfig IceConfig::Parse(const base::DictionaryValue& dictionary) {
+  const base::ListValue* ice_servers_list = nullptr;
+  if (!dictionary.GetList("iceServers", &ice_servers_list)) {
     return IceConfig();
   }
 
@@ -106,7 +104,7 @@
   // Parse lifetimeDuration field.
   std::string lifetime_str;
   base::TimeDelta lifetime;
-  if (!dictionary->GetString("lifetimeDuration", &lifetime_str) ||
+  if (!dictionary.GetString("lifetimeDuration", &lifetime_str) ||
       !ParseLifetime(lifetime_str, &lifetime)) {
     LOG(ERROR) << "Received invalid lifetimeDuration value: " << lifetime_str;
 
@@ -151,7 +149,12 @@
   }
 
   if (errors_found) {
-    LOG(ERROR) << "Received ICE config with errors: " << config_json;
+    std::string json;
+    if (!base::JSONWriter::WriteWithOptions(
+            dictionary, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json)) {
+      NOTREACHED();
+    }
+    LOG(ERROR) << "Received ICE config with errors: " << json;
   }
 
   // If there are no STUN or no TURN servers then mark the config as expired so
@@ -164,5 +167,28 @@
   return ice_config;
 }
 
+// static
+IceConfig IceConfig::Parse(const std::string& config_json) {
+  std::unique_ptr<base::Value> json = base::JSONReader::Read(config_json);
+  if (!json) {
+    return IceConfig();
+  }
+
+  base::DictionaryValue* dictionary = nullptr;
+  if (!json->GetAsDictionary(&dictionary)) {
+    return IceConfig();
+  }
+
+  // Handle the case when the config is wrapped in 'data', i.e. as {'data': {
+  // 'iceServers': {...} }}.
+  base::DictionaryValue* data_dictionary = nullptr;
+  if (!dictionary->HasKey("iceServers") &&
+      dictionary->GetDictionary("data", &data_dictionary)) {
+    return Parse(*data_dictionary);
+  }
+
+  return Parse(*dictionary);
+}
+
 }  // namespace protocol
 }  // namespace remoting
diff --git a/remoting/protocol/ice_config.h b/remoting/protocol/ice_config.h
index d52fd6d..b039ba3f 100644
--- a/remoting/protocol/ice_config.h
+++ b/remoting/protocol/ice_config.h
@@ -12,6 +12,10 @@
 #include "third_party/webrtc/base/socketaddress.h"
 #include "third_party/webrtc/p2p/base/portallocator.h"
 
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
 namespace remoting {
 namespace protocol {
 
@@ -24,6 +28,7 @@
 
   // Parses JSON representation of the config. Returns null config if parsing
   // fails.
+  static IceConfig Parse(const base::DictionaryValue& dictionary);
   static IceConfig Parse(const std::string& config_json);
 
   // Time when the config will stop being valid and need to be refreshed.
diff --git a/remoting/protocol/ice_config_unittest.cc b/remoting/protocol/ice_config_unittest.cc
index fdefe75..ab7aa7c5 100644
--- a/remoting/protocol/ice_config_unittest.cc
+++ b/remoting/protocol/ice_config_unittest.cc
@@ -73,6 +73,25 @@
   EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), config.stun_servers[1]);
 }
 
+TEST(IceConfigTest, ParseDataEnvelope) {
+  const char kTestConfigJson[] =
+      "{\"data\":{"
+      "  \"lifetimeDuration\": \"43200.000s\","
+      "  \"iceServers\": ["
+      "    {"
+      "      \"urls\": ["
+      "        \"stun:1.2.3.4\""
+      "      ]"
+      "    }"
+      "  ]"
+      "}}";
+
+  IceConfig config = IceConfig::Parse(kTestConfigJson);
+
+  EXPECT_EQ(1U, config.stun_servers.size());
+  EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), config.stun_servers[0]);
+}
+
 // Verify that we can still proceed if some servers cannot be parsed.
 TEST(IceConfigTest, ParsePartiallyInvalid) {
   const char kTestConfigJson[] =
diff --git a/remoting/protocol/transport_context.h b/remoting/protocol/transport_context.h
index 2a1ddb6..3d18b29 100644
--- a/remoting/protocol/transport_context.h
+++ b/remoting/protocol/transport_context.h
@@ -51,6 +51,10 @@
                    const NetworkSettings& network_settings,
                    TransportRole role);
 
+  void set_turn_ice_config(const IceConfig& ice_config) {
+    ice_config_[TURN] = ice_config;
+  }
+
   void set_ice_config_url(const std::string& ice_config_url) {
     ice_config_url_ = ice_config_url;
   }
diff --git a/services/BUILD.gn b/services/BUILD.gn
index a46978d..622af1c 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -119,4 +119,21 @@
       "//third_party/android_tools:android_support_annotations_java",
     ]
   }
+
+  android_library("service_javatests") {
+    testonly = true
+    java_files = [ "shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java" ]
+    deps = [
+      "//base:base_java_test_support",
+      "//mojo/public/java:bindings_java",
+      "//services/shape_detection:shape_detection_java",
+      "//services/shape_detection/public/interfaces:interfaces_java",
+      "//skia/public/interfaces:interfaces_java",
+      "//third_party/android_support_test_runner:runner_java",
+      "//ui/gfx/geometry/mojo:mojo_java",
+    ]
+    data = [
+      "test/data/",
+    ]
+  }
 }
diff --git a/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java b/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java
new file mode 100644
index 0000000..b8177d4
--- /dev/null
+++ b/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java
@@ -0,0 +1,86 @@
+// 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.shape_detection;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.test.filters.SmallTest;
+import android.test.InstrumentationTestCase;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.shape_detection.mojom.FaceDetection;
+import org.chromium.shape_detection.mojom.FaceDetectionResult;
+import org.chromium.shape_detection.mojom.FaceDetectorOptions;
+import org.chromium.skia.mojom.ColorType;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test suite for FaceDetectionImpl.
+ */
+public class FaceDetectionImplTest extends InstrumentationTestCase {
+    public static final org.chromium.skia.mojom.Bitmap MONA_LISA_BITMAP =
+            mojoBitmapFromFile("mona_lisa.jpg");
+    // Different versions of Android have different implementations of FaceDetector.findFaces(), so
+    // we have to use a large error threshold.
+    public static final double BOUNDING_BOX_POSITION_ERROR = 10.0;
+    public static final double BOUNDING_BOX_SIZE_ERROR = 5.0;
+
+    public FaceDetectionImplTest() {}
+
+    private static org.chromium.skia.mojom.Bitmap mojoBitmapFromBitmap(Bitmap bitmap) {
+        ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
+        bitmap.copyPixelsToBuffer(buffer);
+
+        org.chromium.skia.mojom.Bitmap mojoBitmap = new org.chromium.skia.mojom.Bitmap();
+        mojoBitmap.width = bitmap.getWidth();
+        mojoBitmap.height = bitmap.getHeight();
+        mojoBitmap.pixelData = buffer.array();
+        mojoBitmap.colorType = ColorType.RGBA_8888;
+        return mojoBitmap;
+    }
+
+    private static org.chromium.skia.mojom.Bitmap mojoBitmapFromFile(String relPath) {
+        String path = UrlUtils.getIsolatedTestFilePath("services/test/data/" + relPath);
+        Bitmap bitmap = BitmapFactory.decodeFile(path);
+        return mojoBitmapFromBitmap(bitmap);
+    }
+
+    private static FaceDetectionResult[] detect(org.chromium.skia.mojom.Bitmap mojoBitmap) {
+        FaceDetectorOptions options = new FaceDetectorOptions();
+        options.fastMode = false;
+        options.maxDetectedFaces = 32;
+        FaceDetection detector = new FaceDetectionImpl(options);
+
+        final ArrayBlockingQueue<FaceDetectionResult[]> queue = new ArrayBlockingQueue<>(1);
+        detector.detect(mojoBitmap, new FaceDetection.DetectResponse() {
+            public void call(FaceDetectionResult[] results) {
+                queue.add(results);
+            }
+        });
+        FaceDetectionResult[] toReturn = null;
+        try {
+            toReturn = queue.poll(5L, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            fail("Could not get FaceDetectionResult: " + e.toString());
+        }
+        assertNotNull(toReturn);
+        return toReturn;
+    }
+
+    @SmallTest
+    @Feature({"ShapeDetection"})
+    public void testDetectSucceedsOnValidImage() {
+        FaceDetectionResult[] results = detect(MONA_LISA_BITMAP);
+        assertEquals(1, results.length);
+        assertEquals(40.0, results[0].boundingBox.width, BOUNDING_BOX_SIZE_ERROR);
+        assertEquals(40.0, results[0].boundingBox.height, BOUNDING_BOX_SIZE_ERROR);
+        assertEquals(24.0, results[0].boundingBox.x, BOUNDING_BOX_POSITION_ERROR);
+        assertEquals(20.0, results[0].boundingBox.y, BOUNDING_BOX_POSITION_ERROR);
+    }
+}
diff --git a/services/shape_detection/face_detection_impl_mac_unittest.mm b/services/shape_detection/face_detection_impl_mac_unittest.mm
index c8c2ed6b..83ea57e 100644
--- a/services/shape_detection/face_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/face_detection_impl_mac_unittest.mm
@@ -28,6 +28,7 @@
 
 // Base64 encoding of the Mona Lisa Face thumbnail from:
 // https://commons.wikimedia.org/wiki/File:Mona_Lisa_face_800x800px.jpg
+// TODO: Use services/test/data/mona_lisa.jpg.  See https://crbug.com/729653.
 const int kJpegImageWidth = 120;
 const int kJpegImageHeight = 120;
 const char kJpegImageInBase64[]="/9j/4AAQSkZJRgABAQEAZABkAAD//gBSRmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOk1vbmFfTGlzYV9mYWNlXzgwMHg4MDBweC5qcGf/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAB4AHgDAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAABQYEBwEDCAIA/8QAOxAAAgECBQIEBAQFAwMFAAAAAQIDBBEABRIhMQZBEyJRYQcycYEUkaGxFUJiwfAjJOEWF9ElM1LC8f/EABoBAAIDAQEAAAAAAAAAAAAAAAIDAQQFAAb/xAAvEQACAgICAgAEBQQCAwAAAAAAAQIRAyESMQRBBRMiUTJhcaGxFCNSgZHwweHx/9oADAMBAAIRAxEAPwBqiIChT5t7gEYwX1ssJUbY0ErKBHpFu498Q3fRKTMmJVNm2POo9/tiG2kTFG+FfIAq9t2ta2BtvsKtkqKmDE2UgMDiUn2C3ugTnvVGRdPy6M2zKFJxv+HivLN6i6LuPvbDsalPaRPy29Cw/wAYOn1kPgUmYS3OxZUjJvwbFrgYfHx8hziqCVF8V+namVUnSvojzqlp9SkD3QkYGWKa62SsY5ZVmVDmlMKnLamCqgvu8bBgPYjkH64U3x/FpkcWF4yT8oG4337YBzdUgVHezEwN1N1BxzurslJGI1uSpJG3bvjlew2bYUN+fS+Cj12DJkqEWNgOeL4OM66AaNscXkb2vziVK47ZD0IkcG1r/X/PthVWFZLRNKqAAdiCT9cC19jk77MRpqILKNjfi/tbCpcug40SooAz6tNyfLsOPbBQhslsrH4n/ECXLa+Tp7IZvBrxaOprEXU0Fx8if1+/a/ri3i8e1zn1/P8A6Ba6oR8h6YqY8szKshpauaWdNCzBHkZifmYk778ffAZvIU5Qg3pMsQg4xbS2KmcZQKPUaqmqItVhqeEqL9rnGhjzc3SaZXyY63QGDvSuJaScofVfMp57YsOPLUkLUnHcQplXUFfQZrFV5bK2XZkll8SEWRx6MnBB9D+mEywppqW0N+by/U6Y+EvXdP1rlrJKkVNnVMAtVTLsrDgSJ30nj2O3pjLz4XhlXr0d+JWh6eJQCy2b6DEOKAT+55hQrYsNrW98TH2S2ToAATYG5GwI4wUqSYPZuSEBNXAIBO2E8eO0TZ8UZVtf5hvbE7RHYkQRG6kix7f2wuMnexjr0SvDZiqpf6dsH30D12bIKe77gna7W7bYHbbCtIFdf9R0/R3S9VmcrIJFHh06t/PKR5Rbk+v0GLMISk1CK2wE03begP8ACHommhy6LM8zjhqM0qf9eeoljEjhm3sL7D7b++KmXK8+Tin9K0l+hcUflwTrbLXFHCABftYdvtthbjRHNgjOsoiqKaaJ9TqykFWAKn88Vsip6HwlfZzV8R+kKeikeSjS128wCD+2NT4f57yfTP0I8jCltFWyJJEXul0vYkLce4JxtqSfRRoJ9K5/WdNdSUOb5a7iWmbUVPEkZI1o3qCt/wBMBmxLLjcP+P1IjOpWztvJa+lzTJ6PMqF9dJVxLLG47qwuPpbi2MqP0Opd9ByV9E2KNWF97AbNiWlu2crN8cIABIFxyL7YW2yTZGhlNr7eltscnZD0ZkpyI++1+cMcVVg3sUUWMxRtHpbVc3GKr4voYrM063k32YEi9t9zhiTXRDJVHAzSB3uoK2F/bEwm4u7OaT0UL8cs9TqHPsqyZEH+nWxaWvuus7KB6keYn6Dti5gySm5Zn0kyflrGlBdtl7dNWp6FFY2awU39RjFxx1aNHJ9g6pdWa5LX3J9MS27aE0uyHXiSRCFN9rG3OEThyGxaQi9UdOpXwyCW1muDb/PbC4J4ZckNdTVM5u63y5cpzeemilCQDzebuff9bY9R4eV5cak+zMzxUJUhVmbUNQt6W9bC++LpWaSOjvgT1JK/w+ho5mtHl9Y1OD6o1pB9hrP5Yy/Mhxm697Gp3Wy0aXNhNMsIYi29u1j2v37Yq7fZLetB01arE5DKVQaRY8m+/wDbHUxTnvQRplvbcaTuL+/GIXdMY2bwbxFd2wXNtUD07KMyvqid3WGnLvqszF0PIB8o/LfEairZNT9D109mYzSmMiRaJUkMbjmxIDCx+hwacpNEKadk3Nq38HlE8oGpvw7sthvcKdsLlG6TCUt0jkbrCsaXramzCGSNr10MmkfNrUqtxfsQBtjS8eP9hwf2f7jJ25KS+6Lv/wC6VBDlQq0lSipEm/DhqpZSddr2IRG08H5iDtxjGXi5XJQjt1fa6L7yQrkx1n6p0dKSZwIwAE4vca+BY973B++KXJyfFd9B/LS2xLTq7NKbIWzvOfHpaeRxGFWkknILfKCFsBe3rg/lKWR48cr/ADbpHcmo3JAOLrjqHPcwio8uyeaohcj/AHUaSJGBv5jrUabW4P64tT8XHjjynk39tX+wr5km6jErP4oUM8Od+JUfM/zC99/rjT+GZYvHSRV8lNSViSmmF2XUrM1wQefe2NTsp7uix/hJWFKDNaUMViM0E5AYgsNJUi/2GKHmR/C/ZPWi5MkzgykxMYlABszCxuP32xVUFsGWR+w1Lm0cNJHGAJRIw3U302bfbne2IpdsSOXT9UKqEsHLJqBH0HbFKVqSTLqaadByJ/D1FgbW2vhuO07BbXRydQZocvqqclJCW31XA3IsSe1zfbFiWHndC1krQfyLrl6TI5KKFNFTGDaULfU1yGJ/NQCe2AyYJJ/T0Hjkktk2l6tmoTmVI0zSrBEqwySG5RmZthc8AXIxPy3KCZy1Oijxoi67yiKUf7eHNI1Z73DDxRck+v8A4xou/wCnlXbi/wCBqf1x17OwP4TBW05RJqiJTJ4jBQu7X3O4Nj7jHmFBTjyTNNzcHRo6ioUk6ckprBYGlBFxfYf/AIMdwaWiLTeyblGXRnLYYWqaiOwsdDDf87jELFyVSOlNx/Caa5abKKdjCZGL3LNI5YnC5pQdRQULkrZyz8YcwNZ1G3hFrIeB2/y2PRfCYccNv2Z/lu5UhEcuXErWYHdibbE/2xq1ZRHz4VpNP1BVU1I0i+PRlyq2a5V1P/2/XFPyopQTfoJLk9Fy5TRVkMjvLTVMpA3ZrHew/I/bvijyW9kSg0uiWIK2PSYaGpYA7CwvxyT3xzURLvsaukq2opJSKiCWJGJtZCdz9sJlFdpjMc2tMbJMxQqbyMCb8jAPI1sclZyPmMBZ2ZZWGkeJqLaix4A+mNODr0IaNbMYiDDJIAbgMBbUpAO4F9r3/K+J2yaT7AGeVdTYtHIG1Pu3qTcbn7n8sWMUVQSb5C3mFayrGYXa8bhyrdiDdbHvxh0YJ6GznpUdk9I9QJmuUUciOC00aEXO1iAf748jNPHcH6dGy6nUgN8QOuafpqloqGty+tkr5m3iiFw68albhhxxv64b4+CedaaSX3ETlHG7e7GLpPMzU9OUlZVRS0krhv8AQlPnUajpv72thTSg2rug/wASTFzrTNZTCywgkr9sLjHnJX6Dk6WjmLrCoaozeWR7sdR2btvwceq8VVjSRlZvxAaWcNEpK2f+axPGLCEMNdLVMkWYRmJn8UkqpHuLf87emByxuLvoh7LbynM8wphrSSrjB8xW1lJ2/wA7YoNKnQDeg7H1DXooAmrFckWUbn9rdsQ8a9CqDtB1XUJvUzh1202sDv625tgJwXVEptK7CY6jlqoisTygggGy7geuFcFdkcm/pbOdpa7xpQzCXzeQG2s27WONCMKHHppnlHhqg1KbhhtfjkG37euJBe2Q81y92y8vM6HUy2AI8p3P9t//ADgoZVy0iYJ3QqThI4NBVZJmc3bkqB6fXFlNt2Ntca9l5fA7qWlzTp6PJ6ipNPmVIDGrgjV4d/Kwvza9vtjznxXBKGR5K+mX8ml4mVTgoe0WR1OlR4UUZrcwqHUXSdcvjk29yLAfljN0pW/5LXoh5DA1NMZszrKyrn07LKiokYHoFHJuNyTiZSjJrVV+oKX52LvXGcwDxI42BYgjbfe22H+Pi5y2LyTpUc/Z9KZcwZ2Yl2a/t7DHqMKSjSMvK9gyVlvZCRtp+vqcNQtumFMhDfjIWifS9wb3taxwvJJKLOirLlpljjdYiulnF/KfNuNhx9+cZnPTdC3HVBCSjUxqZFOlPMCU77knb2xHNN7YFM30ckEQIAXTfY6fMLd8Tr2CyfJnsUKlIULNfsPl55PpxiJQV2FG0ikqKoLlRpRlbSCjC4O/Fr8k+98aEo/caHaWYyOVMOXBRIWCSHYj6ffCZxXabIoL5hl0/wD05WySJQNEkQmURhiWCkG1jyQL++EJrmtjIqip8x8JpnnkRXZyQNPC27n12tjSiq0TrtkGOtlocziqsullp54SGjkDWa+3+Wwc4KUeM9pkW4SuLOi+kfjrlz9PxwZ0r0+ZIgWTSl0f+ofX0x5/P8Kywf8Aa3H+DRx+VCf4tMEZ/wDEuOoB/A6UjI80kjC/5emBx/DZ95Dp511ErvMuojVvppr1EzXJfgL32vzjSx+Lw70V5ZL62LVXSy+O5ZjIxNy9tgMXLVUV3FoinQiG+7WNiB++D7AJuWzGJ1K3BUcn7EYiULVExdOy6qLNoTl1LIWEr2XSVIsGNr77d7jb9cZrwtXE7l7DdVXxVETeV4ybKoQ727m3bfCfkxUrBcrVUQBXwq7GJZmXRqKActf0+ww2Ka0C4Jn1TnETVI0sCC1iBGPU/p+3ptjpJvs5QS9i1Q0IVUctAlmuPCjB/ckg4a5xYwPUpiiZ1srPqHnbw9TahyABzta+Ikv8URFm+oqA0Eo8VDGV0MqkEDm4P14++EO09DE12UbnlGtPn1VTh2aNJLIVTc+VSARtv5gMa2KVwUgX2BZUu6nURcXJY73w7sGSoO9ARUcnWuUQ5giyUk1QIXRvl84Ki/3IxW8vl8mTi6dB4GlkXItXqb4WU8JY0EcSWO5AuAMZGH4nKL4z2Wsnjp7RAynonTIgnjUpcCygj8v3wefzk1pnQxv2TOrOlBT5S/gRqiqoJ2tbfck+mEeN5vLJ9TGSxaKlqKfwigdGUgHy25B4xvRmn0UZRNog8GjSWQeQFjbttYYlTTlR0o0rLL6OzCiqMshK1FPSLpWO7garj5u3YnnFDKppvt//AEFRTHVYacgQ0udQSMynWGS4tzzqxUtydyQTiq0YfpseHLWawaYC6u3kPoCvub4OOWvpbI4ezbTdJxC0r1XhIx1aW8oUn1v/AJvivPzI39DsdHx5PtUVPoneOVpayRKlKZfCaJiCdyTvck2F/uMaXKnpavYiiMnW1Y9OwqxTtO6C0ynQoIuo1AA78/8AGHy8aL6YN0eM16rr5qeHxggpp0CzaYwLMrndTyLGx9MQvHjvfR1+hPramevr6iaSZpJJZS2vgsext9hixGKjFJE22fZbltZmtctLQwy1FQ+yJGNRv+1vfHTyQhHnkdImMJSfFLZevw3+EUcEyV2czh6lfkVR5EbtYnk+/tjB8n4m8v0YVS/cu4vGUPql2XFJl5MAWYaibD5f8tjIk0XEjNNkSIxa2jfa/wCWBa+xNkHPMnSajdbgEIwIbv6XHpgVqXJBJXo5zqsnEWdGjIWS8oVSG+XU9h9bb3HbHpoZ+WHn+RQljqVEHqOkENFDSqiMq1UxW3JF7H7XXj64d487lyv0hWRar8wJk0arVCJ2RUJJ1MdlbixPobYt5HcW0V0iysiyTMa6ro2pqOi0RvdHjk1K+2wIBJIxk5pwjabewoxd2hpHRvU1TmFFXVGZapoTdjqKge3Nj2H0AxXl5eOMWlHsOOCTY7ZjUTUmVwrLL+JrVGyKCdbD1P8ALz3OMzDH63JaTLkp8Y17KG6k8OOKopKGIvII1GuxBtxb2G55/vj0HjqTlym/ZTlVUhPqqA/wWREVS8Uha+m+w2IB/LjGgprmhDTI2awuuVUxmLqI49Kqex1E2PvucdGVzdE1SsG0ccktSqxX1Dvbgep/PBt0rOirejpj4C9IJQ5TLWTxXnqW1K9iCYx8ot6Hn7jHmfifkPNlUU9L+TVwY/lw/Nl009Ivg2CgWsRccHFSNpWiW7dGWijuBGLXbAPYa12bWj0wGx78WxMlUQU9i91AqpSzCUnwyp498VcjldD8Zz7EIaTOqd6dkll/EukjWuCQbkqSeNyfXtvj0D5TxvkvVlZ1FiP1DXvJmwicqRCWN/fUf73xqePjUcdr2UMsvqPa5LVVVG88dNK8RYBNKni3Jt62wXz4xbi2FxbV0Fum80qKevSnqZXpp1kChilib8bYR5GFSi5R2LafRfGW9RR1FHFBUxzyGMJTuIxpAfgAXtfb19748/ODT2WYz0e6l6GOnWVzXxr4nhp8urUb3BBa/wBzhP1ctfuE6rZVOe0aNCskauXSO7Kw0rIO2/pe36Y1sGRp02VpxdWKeeS02V5PGs0aLUMRaPuSMaGLnlyWnoBpRVsQayplq5DJMQVDHSnZfYY0IxSWhTdlp/B3pMZtRzVksPiXa2+1gP2A2NxvjH+JeY8clCJf8TEmuTOm+k8pGU5VBT69ciLubW1H1AxhOTcm37LcpWHwBYDa/e+Du9AL7mDGRp3IOq9sQ4vom/Z9UJqW1rEbHvgZHJ7EvrVo48qqBJJa6FTcHk9vU4RdzikWI9MpSopZ6itCUdGoUEkuyhUDXW9zfbgbcXxtQmlG5S3/AOCrkT6iiZknw5paurmq5JRmFbK5Z1SIyxq1727Ltfe57YXk+JTSUIql+4C8ZW5NhvOsloaOkZMwzGKIR/8AtJNVrCL8GyxrueRtfAYcuSTk4x3+l/yFOMUtspvq5MtjrUejqF8YfM0c0rgenzqP0ON7xnkcWpr+DPy1eiT0v17XZUwhqJmeEEBJlVQwP9RNwdu/IwryPAhkuUdMnHlcdMtTJ/iHPDCGzFEZJPKr+EmpbDctpA59TvjMn4bb+mi1GQKp0myd2rM8rqengC6pUldW2K7WUEkX+vNhbA5az/Rhi2/0ohfRuWim+r83Gd59PVxK0dKvkp47Wsg9vfnHofGwfIxqDdv2Uck+UrQMjjaQrCpbzDg84baQNM6J+Due02Vwfgc5XRS2DxS6SFF+dQ/Kx+2PM/EcDlk+bD/Zq+PlShxZfFPXUtTRGqpqiOSMjV4isCCPrx3xm8lX2D4uyTSFpRrIIU4OKs6TrROATQe/IGHqOhLbs1SeGgYHn62H1wuSDTbK862rqGOOZDLrYA/JJpAJG4v22v72xWWJzmmh/wAxQVMqPMetMloJJHaMZhU2IjhtaFe3H83bnGvj+HZsi/xX7lV+RFO3sWM++JOdVkfgzVy5fSqDppqRLEDi5tsP0Pti/g+F4obrl+bEy8lvrQjVuc1Uxv8AiZyp/pA3+u+NKOCEVVFdzb7YPeeRyLs7G9zra+DSSAb9mI9QG1irHcDg46jhx6Unlqf/AEzYTIC8bjctYElSODtc7+n0xUz41H+5/wB/UtYJuX0P/v5A3rmeq/i38PqleMU41OjC12Nz+gNh9cT4UY8OcfYnNafF+hZbbzXv7W74t0IDmQQJJI81TfSGAuDxte+/+cYRlbWkGl9xsjzYitpIqYMksqDQNfyKCbk32vYHn/5DFT5Sabl6GqdPQcyjNJTI6ZfO1KsVtbU8ugsx33HDADfj88V8mBNXJXY6GV9Idss+IWf5epi/iaT6AAyVFOCRe1hdbeo5v2xVfiQ7imv9jVlT7C8HxZro95IKCcKbF4y4J/Q/TAf0kv8AIJzh3REz74pVE1NIbU9DDGt5JSS+/ZFIFy1+w3+g3wEfDcpV2BLyIpfSVD1D1f40FU5m8JpzaOJV8SZ151MSdMYNztuxxq4PD4ta6/4/19ytPNYjPXSyE+CCt99iSxPqTycaKikV07C2X5bClI1bVgtHTjdb7SPvZfzt+uFSnLkox7YVLshVcaRKseka7NyNgbL/AM4bFtnPQLWOQm6q5ABJ8p49T7e+D5AbZ6VvmNh2sL8Y6yVYRy+qemqaadLiSFw4sObdt8RJcotP2HGTi7XZ/9k=";
diff --git a/services/test/data/mona_lisa.jpg b/services/test/data/mona_lisa.jpg
new file mode 100644
index 0000000..df28704
--- /dev/null
+++ b/services/test/data/mona_lisa.jpg
Binary files differ
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d68a3d9..e0e086b 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4820,12 +4820,6 @@
           "shards": 2
         }
       }
-    ],
-    "scripts": [
-      {
-        "name": "nacl_integration",
-        "script": "nacl_integration.py"
-      }
     ]
   },
   "ClangToTMacASan tester": {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2b206f7c..5cd21e8 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1795,22 +1795,6 @@
             ]
         }
     ],
-    "OfflinePageCache": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OfflineBookmarks",
-                        "OfflineRecentPages"
-                    ]
-                }
-            ]
-        }
-    ],
     "OfflinePages": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 826cbfcc..058af07 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -914,8 +914,6 @@
 
 crbug.com/498539 [ Win7 ] inspector/elements/styles-4/styles-update-from-js.html [ Crash Pass ]
 
-crbug.com/731144 inspector/elements/edit/set-outer-html-2.html [ Failure Pass ]
-
 crbug.com/596968 [ Win ] inspector-protocol/input/eventTimestamp.html [ Failure Pass ]
 
 crbug.com/487281 [ Mac ] fast/forms/select/menulist-narrow-width.html [ Failure ]
@@ -2098,7 +2096,6 @@
 crbug.com/626703 external/wpt/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html [ Timeout ]
 crbug.com/626703 external/wpt/webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueCurveAtTime.html [ Timeout ]
 crbug.com/626703 external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html [ Timeout ]
-crbug.com/626703 external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html [ Timeout ]
 crbug.com/626703 external/wpt/webauthn/interfaces.https.html [ Timeout ]
 crbug.com/626703 external/wpt/webauthn/makecredential-badargs-accountinformation.https.html [ Timeout ]
 crbug.com/626703 external/wpt/webauthn/makecredential-badargs-attestationchallenge.https.html [ Timeout ]
@@ -2509,11 +2506,10 @@
 crbug.com/713891 virtual/exotic-color-space/images/animated-png.html [ Pass Failure ]
 crbug.com/713891 virtual/exotic-color-space/images/color-profile-border-image.html [ Pass Failure ]
 crbug.com/713891 virtual/exotic-color-space/images/color-profile-group.html [ Pass Failure ]
-crbug.com/713891 virtual/exotic-color-space/images/color-profile-munsell-adobe-to-srgb.html [ Pass Failure ]
-crbug.com/713891 virtual/exotic-color-space/images/color-profile-munsell-srgb-to-srgb.html [ Pass Failure ]
+crbug.com/731211 virtual/exotic-color-space/images/color-profile-munsell-adobe-to-srgb.html [ Pass Failure Timeout ]
+crbug.com/731211 virtual/exotic-color-space/images/color-profile-munsell-srgb-to-srgb.html [ Pass Failure Timeout ]
 crbug.com/731153 [ Mac10.9 ] virtual/exotic-color-space/images/color-profile-reflection.html [ Failure ]
 
-
 # [selectors-4]
 crbug.com/706118 external/wpt/css/selectors4/hover-001-manual.html [ Skip ]
 crbug.com/576815 external/wpt/css/selectors4/selectors-dir-selector-ltr-001.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
index 791b74a6..ad31366 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
@@ -111,16 +111,7 @@
 			Any sample value less than -1 will correspond to the first value in the curve array.
 			Any sample value greater than +1 will correspond to the last value in the curve array.
 			The implementation must perform linear interpolation between adjacent points in the curve.
-		Note:
-			I found a post on the W3C audio mailing list (from one of the Chris's) that suggested it would be feasible
-				to use the WaveShaperNode to create constant values.
 		*/
-		(function() {
-			var oneElementCurve=[1.0];
-			var inputData=[-1.0, 0, 1.0, -2.0, 2.0];
-			var expectedData=[1.0, 1.0, 1.0, 1.0, 1.0];
-			executeTest(oneElementCurve, inputData, expectedData, "Testing single-element curve (boundary condition)");
-		})();
 
 		/*
 		Testing null curve (should return input values)
@@ -135,22 +126,6 @@
 			executeTest(null, inputData, expectedData, "Testing null curve (should return input values)");
 		})();
 
-		/*
-		Testing zero-element curve (unspecified result)
-		===============================================
-		From the specification:
-			Unspecified result (I assume it will be treated in the same way as a null curve).
-		Note:
-			Mozilla test_waveShaperNoCurve.html indicates they expect same results as a null curve.
-		*/
-		(function() {
-			var zeroElementCurve=[];
-			var inputData=[-1.0, 0, 1.0, 2.0];
-			var expectedData=[-1.0, 0.0, 1.0, 2.0];
-			executeTest(zeroElementCurve, inputData, expectedData, "Testing zero-element curve (unspecified result)");
-		})();
-
-
 		/**
 		* Function that does the actual testing (using an asynchronous test).
 		* @param {?Array.<number>} curveData - Array containing values for the WaveShaper curve.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt
index c77fa42..27a18046 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt
@@ -1,15 +1,15 @@
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback4
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback5
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback6
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback7
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback8
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback9
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback10
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback11
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback12
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback7
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback8
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback9
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback10
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback11
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback12
 Tests that data is correctly loaded by IndexedDBModel from IndexedDB object store and index.
 
 Dumping values, fromIndex = false, skipCount = 0, pageSize = 2, idbKeyRange = null
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-names-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-names-expected.txt
index 8b19b03755..1246cb45 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-names-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-names-expected.txt
@@ -1,7 +1,7 @@
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback4
 Tests that database names are correctly loaded and saved in IndexedDBModel.
 
 Dumping database names:
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view-expected.txt
index f374ce0..0f281a5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view-expected.txt
@@ -24,10 +24,22 @@
             (no entries)
     Object store: testObjectStore2
             (no entries)
-Refreshed database.
+Refreshed database view.
 Dumping ObjectStore data:
     Object store: testObjectStore1
             Key = testKey, value = [object Object]
     Object store: testObjectStore2
             (no entries)
+Added testObjectStore2 entry.
+Dumping ObjectStore data:
+    Object store: testObjectStore1
+            Key = testKey, value = [object Object]
+    Object store: testObjectStore2
+            (no entries)
+Right-click refreshed database.
+Dumping ObjectStore data:
+    Object store: testObjectStore1
+            Key = testKey, value = [object Object]
+    Object store: testObjectStore2
+            Key = testKey2, value = [object Object]
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view.html b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view.html
index d9c3bb0..f9fb0a8c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-refresh-view.html
@@ -6,70 +6,6 @@
 <script src="indexeddb-test.js"></script>
 <script>
 
-function onIndexedDBError(e) {
-    console.error("IndexedDB error: " + e);
-}
-
-function createDatabase(databaseName) {
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    var request = indexedDB.open(databaseName);
-    request.onerror = onIndexedDBError;
-    request.onsuccess = function(event) {
-        request.result.close();
-        callback();
-    }
-    return promise;
-}
-
-function createObjectStore(databaseName, objectStoreName, indexName, keyPath) {
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    var request = indexedDB.open(databaseName);
-    request.onerror = onIndexedDBError;
-    request.onsuccess = function(event) {
-        var db = request.result;
-        var version = db.version;
-        db.close();
-
-        var upgradeRequest = indexedDB.open(databaseName, version + 1);
-
-        upgradeRequest.onerror = onIndexedDBError;
-        upgradeRequest.onupgradeneeded = function(e) {
-            var upgradeDb = e.target.result;
-            var store = upgradeDb.createObjectStore(objectStoreName, { keyPath: "test", autoIncrement: false });
-            store.createIndex(indexName, "test", { unique: false, multiEntry: false });
-            callback();
-        }
-        upgradeRequest.onsuccess = function(e) {
-            var upgradeDb = e.target.result;
-            upgradeDb.close();
-            callback();
-        }
-    }
-    return promise;
-}
-
-function addIDBValue(databaseName, objectStoreName, key, value) {
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    var request = indexedDB.open(databaseName);
-    request.onerror = onIndexedDBError;
-    request.onsuccess = function(event) {
-        var db = request.result;
-        var transaction = db.transaction(objectStoreName, "readwrite");
-        var store = transaction.objectStore(objectStoreName);
-        store.put({ test: key, testValue: value });
-
-        transaction.onerror = onIndexedDBError;
-        transaction.oncomplete = function() {
-            db.close();
-            callback();
-        };
-    }
-    return promise;
-}
-
 async function test()
 {
     var databaseName = "testDatabase";
@@ -81,14 +17,23 @@
     var indexedDBModel = InspectorTest.mainTarget.model(Resources.IndexedDBModel);
     var databaseId;
 
-    function waitRefreshDatabase(callback) {
+    function waitRefreshDatabase() {
         var view = UI.panels.resources._sidebar.indexedDBListTreeElement._idbDatabaseTreeElements[0]._view;
-        InspectorTest.addSniffer(Resources.IDBDatabaseView.prototype, "_updatedForTests", callback, false);
         view._refreshDatabaseButtonClicked();
+        return new Promise((resolve) => {
+            InspectorTest.addSniffer(Resources.IDBDatabaseView.prototype, "_updatedForTests", resolve, false)
+        });
     }
 
-    function waitUpdateDataView(callback) {
-        InspectorTest.addSniffer(Resources.IDBDataView.prototype, "_updatedDataForTests", callback, false);
+    function waitRefreshDatabaseRightClick() {
+        idbDatabaseTreeElement._refreshIndexedDB();
+        return waitUpdateDataView();
+    }
+
+    function waitUpdateDataView() {
+        return new Promise((resolve) => {
+            InspectorTest.addSniffer(Resources.IDBDataView.prototype, "_updatedDataForTests", resolve, false)
+        });
     }
 
     function waitDatabaseLoaded(callback) {
@@ -128,7 +73,7 @@
     InspectorTest.dumpIndexedDBTree();
 
     // Create database
-    await InspectorTest.evaluateInPageAsync("createDatabase('" + databaseName + "')");
+    await InspectorTest.createDatabaseAsync(databaseName);
     await new Promise(waitDatabaseAdded);
     var idbDatabaseTreeElement = UI.panels.resources._sidebar.indexedDBListTreeElement._idbDatabaseTreeElements[0];
     databaseId = idbDatabaseTreeElement._databaseId;
@@ -142,14 +87,14 @@
     var databaseView = idbDatabaseTreeElement._view;
 
     // Create first objectstore
-    await InspectorTest.evaluateInPageAsync("createObjectStore('" + databaseName + "', '" + objectStoreName1 + "', '" + indexName + "', '" + keyPath + "')");
-    await new Promise(waitRefreshDatabase);
+    await InspectorTest.createObjectStoreAsync(databaseName, objectStoreName1, indexName, keyPath);
+    await waitRefreshDatabase();
     InspectorTest.addResult("Created first objectstore.");
     InspectorTest.dumpIndexedDBTree();
 
     // Create second objectstore
-    await InspectorTest.evaluateInPageAsync("createObjectStore('" + databaseName + "', '" + objectStoreName2 + "', '" + indexName + "', '" + keyPath + "')");
-    await new Promise(waitRefreshDatabase);
+    await InspectorTest.createObjectStoreAsync(databaseName, objectStoreName2, indexName, keyPath);
+    await waitRefreshDatabase();
     InspectorTest.addResult("Created second objectstore.");
     InspectorTest.dumpIndexedDBTree();
 
@@ -160,21 +105,25 @@
     }
 
     // Add entries
-    await InspectorTest.evaluateInPageAsync("addIDBValue('" + databaseName + "', '" + objectStoreName1 + "', 'testKey', 'testValue')");
+    await InspectorTest.addIDBValueAsync(databaseName, objectStoreName1, "testKey", "testValue");
     InspectorTest.addResult("Added " + objectStoreName1 + " entry.");
     dumpObjectStores();
 
     // Refresh database view
-    await new Promise(waitRefreshDatabase);
-    for (var i = 0; i < idbDatabaseTreeElement.childCount(); ++i) {
-        var objectStoreTreeElement = idbDatabaseTreeElement.childAt(i);
-        if (objectStoreTreeElement._objectStore.name === objectStoreName1) {
-            objectStoreTreeElement.onselect(false);
-            break;
-        }
-    }
-    await new Promise(waitUpdateDataView);      // Wait for objectstore data to load on page.
-    InspectorTest.addResult("Refreshed database.");
+    await waitRefreshDatabase();
+    await waitUpdateDataView();  // Wait for second objectstore data to load on page.
+    InspectorTest.addResult("Refreshed database view.");
+    dumpObjectStores();
+
+    // Add entries
+    await InspectorTest.addIDBValueAsync(databaseName, objectStoreName2, "testKey2", "testValue2");
+    InspectorTest.addResult("Added " + objectStoreName2 + " entry.");
+    dumpObjectStores();
+
+    // Right-click refresh database view
+    await waitRefreshDatabaseRightClick();
+    await waitUpdateDataView();  // Wait for second objectstore data to load on page.
+    InspectorTest.addResult("Right-click refreshed database.");
     dumpObjectStores();
 
     InspectorTest.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-structure-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-structure-expected.txt
index e3eb3df2..59ca79c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-structure-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-structure-expected.txt
@@ -1,13 +1,13 @@
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback4
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback5
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback6
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback7
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback8
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback9
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback10
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback7
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback8
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback9
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback10
 Tests that database names are correctly loaded and saved in IndexedDBModel.
 
 Dumping database:
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/indexeddb-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/indexeddb-test.js
index cf36c59a..0efa65a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/indexeddb-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/indexeddb-test.js
@@ -105,6 +105,18 @@
     indexedDBModel.enable();
     return indexedDBModel;
 };
+
+InspectorTest.createDatabaseAsync = function(databaseName) {
+    return InspectorTest.evaluateInPageAsync("createDatabaseAsync('" + databaseName + "')");
+};
+
+InspectorTest.createObjectStoreAsync = function(databaseName, objectStoreName, indexName, keyPath) {
+    return InspectorTest.evaluateInPageAsync("createObjectStoreAsync('" + databaseName + "', '" + objectStoreName + "', '" + indexName + "', '" + keyPath + "')");
+};
+
+InspectorTest.addIDBValueAsync = function(databaseName, objectStoreName, key, value) {
+    return InspectorTest.evaluateInPageAsync("addIDBValueAsync('" + databaseName + "', '" + objectStoreName + "', '" + key + "', '" + value + "')");
+};
 };
 
 function dispatchCallback(callbackId)
@@ -260,3 +272,63 @@
         request.onsuccess = commitCallback;
     }
 }
+
+function createDatabaseAsync(databaseName) {
+    var callback;
+    var promise = new Promise((fulfill) => callback = fulfill);
+    var request = indexedDB.open(databaseName);
+    request.onerror = onIndexedDBError;
+    request.onsuccess = function(event) {
+        request.result.close();
+        callback();
+    }
+    return promise;
+}
+
+function createObjectStoreAsync(databaseName, objectStoreName, indexName, keyPath) {
+    var callback;
+    var promise = new Promise((fulfill) => callback = fulfill);
+    var request = indexedDB.open(databaseName);
+    request.onerror = onIndexedDBError;
+    request.onsuccess = function(event) {
+        var db = request.result;
+        var version = db.version;
+        db.close();
+
+        var upgradeRequest = indexedDB.open(databaseName, version + 1);
+
+        upgradeRequest.onerror = onIndexedDBError;
+        upgradeRequest.onupgradeneeded = function(e) {
+            var upgradeDb = e.target.result;
+            var store = upgradeDb.createObjectStore(objectStoreName, { keyPath: "test", autoIncrement: false });
+            store.createIndex(indexName, "test", { unique: false, multiEntry: false });
+            callback();
+        }
+        upgradeRequest.onsuccess = function(e) {
+            var upgradeDb = e.target.result;
+            upgradeDb.close();
+            callback();
+        }
+    }
+    return promise;
+}
+
+function addIDBValueAsync(databaseName, objectStoreName, key, value) {
+    var callback;
+    var promise = new Promise((fulfill) => callback = fulfill);
+    var request = indexedDB.open(databaseName);
+    request.onerror = onIndexedDBError;
+    request.onsuccess = function(event) {
+        var db = request.result;
+        var transaction = db.transaction(objectStoreName, "readwrite");
+        var store = transaction.objectStore(objectStoreName);
+        store.put({ test: key, testValue: value });
+
+        transaction.onerror = onIndexedDBError;
+        transaction.oncomplete = function() {
+            db.close();
+            callback();
+        };
+    }
+    return promise;
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/resources-panel-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/resources-panel-expected.txt
index f85d717..3d2f53fa 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/resources-panel-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/resources-panel-expected.txt
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback4
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback5
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback6
 Tests IndexedDB tree element on resources panel.
 
 Expanded IndexedDB tree element.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/upgrade-events-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/upgrade-events-expected.txt
index 3151189..96b11bd8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/upgrade-events-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/upgrade-events-expected.txt
@@ -1,13 +1,13 @@
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback4
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback5
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback6
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback7
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback8
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback9
-CONSOLE MESSAGE: line 112: InspectorTest.IndexedDB_callback10
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback7
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback8
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback9
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback10
 Tests that deleted databases do not get recreated.
 
 Preparing database
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/edit/set-outer-html-2.html b/third_party/WebKit/LayoutTests/inspector/elements/edit/set-outer-html-2.html
index 2f267c5..bee53ba6 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/edit/set-outer-html-2.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/edit/set-outer-html-2.html
@@ -14,7 +14,6 @@
 
 function test()
 {
-setTimeout(() => InspectorTest.completeTest(), 2000);
     InspectorTest.runTestSuite([
         function testSetUp(next)
         {
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
index 3eafdb78..13c92f9 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
@@ -1127,9 +1127,12 @@
   if (!client_)
     return;
 
-  RefPtr<const SharedBuffer> data = resource->ResourceBuffer();
-  if (data)
-    HandleReceivedData(data->Data(), data->size());
+  if (RefPtr<const SharedBuffer> data = resource->ResourceBuffer()) {
+    data->ForEachSegment([this](const char* segment, size_t segment_size,
+                                size_t segment_offset) {
+      HandleReceivedData(segment, segment_size);
+    });
+  }
 
   // The client may cancel this loader in handleReceivedData(). In such a case,
   // skip the rest.
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index 176238c..83530023 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -23,6 +23,7 @@
 #include "platform/graphics/paint/DrawingRecorder.h"
 #include "platform/graphics/paint/PaintRecord.h"
 #include "platform/graphics/paint/PaintRecorder.h"
+#include "platform/graphics/paint/PaintShader.h"
 #include "platform/wtf/Optional.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 
@@ -768,7 +769,7 @@
   PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setColor(color);
-  flags.setShader(SkGradientShader::MakeLinear(
+  flags.setShader(PaintShader::MakeLinearGradient(
       pts, colors, nullptr, ARRAY_SIZE(colors), SkShader::kClamp_TileMode));
   PaintRecorder recorder;
   recorder.beginRecording(kMarkerWidth, kMarkerHeight);
@@ -812,9 +813,9 @@
 
   PaintFlags flags;
   flags.setAntiAlias(true);
-  flags.setShader(WrapSkShader(MakePaintShaderRecord(
+  flags.setShader(PaintShader::MakePaintRecord(
       sk_ref_sp(marker), FloatRect(0, 0, kMarkerWidth, kMarkerHeight),
-      SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode, &local_matrix)));
+      SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode, &local_matrix));
 
   // Apply the origin translation as a global transform.  This ensures that the
   // shader local matrix depends solely on zoom => Skia can reuse the same
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
index 26aa2ca..0bdff49 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
@@ -355,13 +355,13 @@
                                  phase.Y() + spaced_tile.Y());
 
   PaintFlags flags;
-  flags.setShader(
-      MakePaintShaderRecord(record, spaced_tile, SkShader::kRepeat_TileMode,
-                            SkShader::kRepeat_TileMode, &pattern_transform));
+  flags.setShader(PaintShader::MakePaintRecord(
+      record, spaced_tile, SkShader::kRepeat_TileMode,
+      SkShader::kRepeat_TileMode, &pattern_transform));
   // If the shader could not be instantiated (e.g. non-invertible matrix),
   // draw transparent.
   // Note: we can't simply bail, because of arbitrary blend mode.
-  if (!flags.getShader())
+  if (!flags.HasShader())
     flags.setColor(SK_ColorTRANSPARENT);
 
   flags.setBlendMode(composite_op);
@@ -403,7 +403,7 @@
 
   IntRect bounds(IntPoint(), size);
 
-  flags.setShader(MakePaintShaderRecord(
+  flags.setShader(PaintShader::MakePaintRecord(
       PaintRecordForCurrentFrame(bounds, url), bounds,
       SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &local_matrix));
 
diff --git a/third_party/WebKit/Source/devtools/front_end/common/UIString.js b/third_party/WebKit/Source/devtools/front_end/common/UIString.js
index 45f73639..9333b76 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/UIString.js
+++ b/third_party/WebKit/Source/devtools/front_end/common/UIString.js
@@ -42,34 +42,6 @@
 
 /**
  * @param {string} string
- * @param {...*} vararg
- * @return {string}
- */
-Common.UIString.capitalize = function(string, vararg) {
-  if (Common._useLowerCaseMenuTitles === undefined)
-    throw 'Common.setLocalizationPlatform() has not been called';
-
-  var localized = Common.localize(string);
-  var capitalized;
-  if (Common._useLowerCaseMenuTitles) {
-    capitalized = localized.replace(/\^(.)/g, '$1');
-  } else {
-    capitalized = localized.replace(/\^(.)/g, function(str, char) {
-      return char.toUpperCase();
-    });
-  }
-  return String.vsprintf(capitalized, Array.prototype.slice.call(arguments, 1));
-};
-
-/**
- * @param {string} platform
- */
-Common.setLocalizationPlatform = function(platform) {
-  Common._useLowerCaseMenuTitles = platform === 'windows';
-};
-
-/**
- * @param {string} string
  * @return {string}
  */
 Common.localize = function(string) {
diff --git a/third_party/WebKit/Source/devtools/front_end/components/DOMBreakpointsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/components/DOMBreakpointsSidebarPane.js
index 5c4d60d..dd56c97 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/DOMBreakpointsSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/DOMBreakpointsSidebarPane.js
@@ -174,10 +174,10 @@
    */
   _contextMenu(breakpoint, event) {
     var contextMenu = new UI.ContextMenu(event);
-    contextMenu.appendItem(Common.UIString.capitalize('Remove ^breakpoint'), () => {
+    contextMenu.appendItem(Common.UIString('Remove breakpoint'), () => {
       breakpoint.domDebuggerModel.removeDOMBreakpoint(breakpoint.node, breakpoint.type);
     });
-    contextMenu.appendItem(Common.UIString.capitalize('Remove ^all DOM breakpoints'), () => {
+    contextMenu.appendItem(Common.UIString('Remove all DOM breakpoints'), () => {
       breakpoint.domDebuggerModel.removeAllDOMBreakpoints();
     });
     contextMenu.show();
diff --git a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
index 7517eb3b..b5794649 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
@@ -551,30 +551,22 @@
 
     if (info.revealable)
       result.push({title: Common.UIString('Reveal'), handler: () => Common.Revealer.reveal(info.revealable)});
-    if (uiLocation) {
-      result.push({
-        title: Common.UIString.capitalize('Open in Sources ^panel'),
-        handler: () => Common.Revealer.reveal(uiLocation)
-      });
-    }
+    if (uiLocation)
+      result.push({title: Common.UIString('Open in Sources panel'), handler: () => Common.Revealer.reveal(uiLocation)});
+
     if (resource) {
-      result.push({
-        title: Common.UIString.capitalize('Open in Application ^panel'),
-        handler: () => Common.Revealer.reveal(resource)
-      });
+      result.push(
+          {title: Common.UIString('Open in Application panel'), handler: () => Common.Revealer.reveal(resource)});
     }
-    if (request) {
-      result.push({
-        title: Common.UIString.capitalize('Open in Network ^panel'),
-        handler: () => Common.Revealer.reveal(request)
-      });
-    }
+    if (request)
+      result.push({title: Common.UIString('Open in Network panel'), handler: () => Common.Revealer.reveal(request)});
+
     if (contentProvider) {
       var lineNumber = uiLocation ? uiLocation.lineNumber : info.lineNumber || 0;
       for (var title of Components.Linkifier._linkHandlers.keys()) {
         var handler = Components.Linkifier._linkHandlers.get(title);
         var action = {
-          title: Common.UIString.capitalize('Open using %s', title),
+          title: Common.UIString('Open using %s', title),
           handler: handler.bind(null, contentProvider, lineNumber)
         };
         if (title === Components.Linkifier._linkHandlerSetting().get())
@@ -743,8 +735,7 @@
         UI.openLinkExternallyLabel(), () => InspectorFrontendHost.openInNewTab(contentProvider.contentURL()));
     for (var title of Components.Linkifier._linkHandlers.keys()) {
       var handler = Components.Linkifier._linkHandlers.get(title);
-      contextMenu.appendItem(
-          Common.UIString.capitalize('Open using %s', title), handler.bind(null, contentProvider, 0));
+      contextMenu.appendItem(Common.UIString('Open using %s', title), handler.bind(null, contentProvider, 0));
     }
     if (contentProvider instanceof SDK.NetworkRequest)
       return;
@@ -785,7 +776,7 @@
     if (contentProvider instanceof Workspace.UISourceCode) {
       var uiSourceCode = /** @type {!Workspace.UISourceCode} */ (contentProvider);
       if (!uiSourceCode.project().canSetFileContent())
-        contextMenu.appendItem(Common.UIString.capitalize('Save ^as...'), save.bind(null, true));
+        contextMenu.appendItem(Common.UIString('Save as...'), save.bind(null, true));
     }
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
index 3cc5a05..49c2cea 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -548,14 +548,13 @@
     var filterSubMenu = contextMenu.appendSubMenuItem(Common.UIString('Filter'));
 
     if (consoleMessage && consoleMessage.url) {
-      var menuTitle =
-          Common.UIString.capitalize('Hide ^messages from %s', new Common.ParsedURL(consoleMessage.url).displayName);
+      var menuTitle = Common.UIString('Hide messages from %s', new Common.ParsedURL(consoleMessage.url).displayName);
       filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
     }
 
     filterSubMenu.appendSeparator();
-    var unhideAll = filterSubMenu.appendItem(
-        Common.UIString.capitalize('Unhide ^all'), this._filter.removeMessageURLFilter.bind(this._filter));
+    var unhideAll =
+        filterSubMenu.appendItem(Common.UIString('Unhide all'), this._filter.removeMessageURLFilter.bind(this._filter));
     filterSubMenu.appendSeparator();
 
     var hasFilters = false;
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
index d1c6472..3e5c12c 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
@@ -1011,7 +1011,7 @@
     if (gridNode && gridNode.selectable && !gridNode.isEventWithinDisclosureTriangle(event)) {
       if (this._editCallback) {
         if (gridNode === this.creationNode) {
-          contextMenu.appendItem(Common.UIString.capitalize('Add ^new'), this._startEditing.bind(this, target));
+          contextMenu.appendItem(Common.UIString('Add new'), this._startEditing.bind(this, target));
         } else {
           var columnId = this.columnIdFromNode(target);
           if (columnId && this._columns[columnId].editable) {
@@ -1021,7 +1021,7 @@
         }
       }
       if (this._deleteCallback && gridNode !== this.creationNode)
-        contextMenu.appendItem(Common.UIString.capitalize('Delete'), this._deleteCallback.bind(this, gridNode));
+        contextMenu.appendItem(Common.UIString('Delete'), this._deleteCallback.bind(this, gridNode));
       if (this._rowContextMenuCallback)
         this._rowContextMenuCallback(contextMenu, gridNode);
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
index 2833117..fe79eb0 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
@@ -872,7 +872,7 @@
     if (Elements.ElementsPanel.instance().element.isAncestor(/** @type {!Node} */ (event.target)))
       return;
     var commandCallback = Common.Revealer.reveal.bind(Common.Revealer, object);
-    contextMenu.appendItem(Common.UIString.capitalize('Reveal in Elements ^panel'), commandCallback);
+    contextMenu.appendItem(Common.UIString('Reveal in Elements panel'), commandCallback);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/MediaQueryInspector.js b/third_party/WebKit/Source/devtools/front_end/emulation/MediaQueryInspector.js
index 1ac838a..db09f98a 100644
--- a/third_party/WebKit/Source/devtools/front_end/emulation/MediaQueryInspector.js
+++ b/third_party/WebKit/Source/devtools/front_end/emulation/MediaQueryInspector.js
@@ -115,7 +115,7 @@
 
     var contextMenuItems = uiLocations.keysArray().sort();
     var contextMenu = new UI.ContextMenu(event);
-    var subMenuItem = contextMenu.appendSubMenuItem(Common.UIString.capitalize('Reveal in ^source ^code'));
+    var subMenuItem = contextMenu.appendSubMenuItem(Common.UIString('Reveal in source code'));
     for (var i = 0; i < contextMenuItems.length; ++i) {
       var title = contextMenuItems[i];
       subMenuItem.appendItem(
diff --git a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
index 197b438..7573ae5 100644
--- a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
+++ b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
@@ -526,7 +526,6 @@
   // so the host instance should not initialized there.
   initializeInspectorFrontendHost();
   window.InspectorFrontendAPI = new Host.InspectorFrontendAPIImpl();
-  Common.setLocalizationPlatform(InspectorFrontendHost.platform());
 })();
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
index 16f0bfbb..c15bedf 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -1103,16 +1103,14 @@
           UI.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, request.contentURL()));
       copyMenu.appendSeparator();
 
-      if (request.requestHeadersText()) {
-        copyMenu.appendItem(
-            Common.UIString.capitalize('Copy ^request ^headers'), this._copyRequestHeaders.bind(this, request));
-      }
-      if (request.responseHeadersText) {
-        copyMenu.appendItem(
-            Common.UIString.capitalize('Copy ^response ^headers'), this._copyResponseHeaders.bind(this, request));
-      }
+      if (request.requestHeadersText())
+        copyMenu.appendItem(Common.UIString('Copy request headers'), this._copyRequestHeaders.bind(this, request));
+
+      if (request.responseHeadersText)
+        copyMenu.appendItem(Common.UIString('Copy response headers'), this._copyResponseHeaders.bind(this, request));
+
       if (request.finished)
-        copyMenu.appendItem(Common.UIString.capitalize('Copy ^response'), this._copyResponse.bind(this, request));
+        copyMenu.appendItem(Common.UIString('Copy response'), this._copyResponse.bind(this, request));
 
       if (Host.isWin()) {
         copyMenu.appendItem(Common.UIString('Copy as cURL (cmd)'), this._copyCurlCommand.bind(this, request, 'win'));
@@ -1126,14 +1124,14 @@
     } else {
       copyMenu = contextMenu.appendSubMenuItem(Common.UIString('Copy'));
     }
-    copyMenu.appendItem(Common.UIString.capitalize('Copy ^all as HAR'), this._copyAll.bind(this));
+    copyMenu.appendItem(Common.UIString('Copy all as HAR'), this._copyAll.bind(this));
 
     contextMenu.appendSeparator();
-    contextMenu.appendItem(Common.UIString.capitalize('Save as HAR with ^content'), this._exportAll.bind(this));
+    contextMenu.appendItem(Common.UIString('Save as HAR with content'), this._exportAll.bind(this));
 
     contextMenu.appendSeparator();
-    contextMenu.appendItem(Common.UIString.capitalize('Clear ^browser ^cache'), this._clearBrowserCache.bind(this));
-    contextMenu.appendItem(Common.UIString.capitalize('Clear ^browser ^cookies'), this._clearBrowserCookies.bind(this));
+    contextMenu.appendItem(Common.UIString('Clear browser cache'), this._clearBrowserCache.bind(this));
+    contextMenu.appendItem(Common.UIString('Clear browser cookies'), this._clearBrowserCookies.bind(this));
 
     if (request) {
       contextMenu.appendSeparator();
@@ -1144,21 +1142,19 @@
 
       var urlWithoutScheme = request.parsedURL.urlWithoutScheme();
       if (urlWithoutScheme && !patterns.find(pattern => pattern.url === urlWithoutScheme)) {
-        contextMenu.appendItem(
-            Common.UIString.capitalize('Block ^request URL'), addBlockedURL.bind(null, urlWithoutScheme));
+        contextMenu.appendItem(Common.UIString('Block request URL'), addBlockedURL.bind(null, urlWithoutScheme));
       } else if (urlWithoutScheme) {
         const croppedURL = urlWithoutScheme.trimMiddle(maxBlockedURLLength);
         contextMenu.appendItem(
-            Common.UIString.capitalize('Unblock ' + croppedURL), removeBlockedURL.bind(null, urlWithoutScheme));
+            Common.UIString('Unblock %s', croppedURL), removeBlockedURL.bind(null, urlWithoutScheme));
       }
 
       var domain = request.parsedURL.domain();
       if (domain && !patterns.find(pattern => pattern.url === domain)) {
-        contextMenu.appendItem(Common.UIString.capitalize('Block ^request ^domain'), addBlockedURL.bind(null, domain));
+        contextMenu.appendItem(Common.UIString('Block request domain'), addBlockedURL.bind(null, domain));
       } else if (domain) {
         const croppedDomain = domain.trimMiddle(maxBlockedURLLength);
-        contextMenu.appendItem(
-            Common.UIString.capitalize('Unblock ' + croppedDomain), removeBlockedURL.bind(null, domain));
+        contextMenu.appendItem(Common.UIString('Unblock %s', croppedDomain), removeBlockedURL.bind(null, domain));
       }
 
       if (SDK.NetworkManager.canReplayRequest(request)) {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
index 21e4f43..aed4007 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
@@ -504,7 +504,7 @@
      * @this {Network.NetworkPanel}
      */
     function appendRevealItem(request) {
-      contextMenu.appendItem(Common.UIString.capitalize('Reveal in Network ^panel'), reveal.bind(this, request));
+      contextMenu.appendItem(Common.UIString('Reveal in Network panel'), reveal.bind(this, request));
     }
 
     if (event.target.isSelfOrDescendant(this.element))
diff --git a/third_party/WebKit/Source/devtools/front_end/network/ResourceWebSocketFrameView.js b/third_party/WebKit/Source/devtools/front_end/network/ResourceWebSocketFrameView.js
index 194848ad..f2d959ac 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/ResourceWebSocketFrameView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/ResourceWebSocketFrameView.js
@@ -96,10 +96,9 @@
      */
     function onRowContextMenu(contextMenu, node) {
       contextMenu.appendItem(
-          Common.UIString.capitalize('Copy ^message'),
-          InspectorFrontendHost.copyText.bind(InspectorFrontendHost, node.data.data));
+          Common.UIString('Copy message'), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, node.data.data));
       contextMenu.appendSeparator();
-      contextMenu.appendItem(Common.UIString.capitalize('Clear ^all'), this._clearFrames.bind(this));
+      contextMenu.appendItem(Common.UIString('Clear all'), this._clearFrames.bind(this));
     }
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js b/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js
index d8c31e3..9d8befa 100644
--- a/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js
+++ b/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js
@@ -226,7 +226,7 @@
   _contextMenuEventFired(event) {
     var contextMenu = new UI.ContextMenu(event);
     if (this._customPreviewSection)
-      contextMenu.appendItem(Common.UIString.capitalize('Show as Javascript ^object'), this._disassemble.bind(this));
+      contextMenu.appendItem(Common.UIString('Show as JavaScript object'), this._disassemble.bind(this));
     contextMenu.appendApplicableItems(this._object);
     contextMenu.show();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js b/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js
index 861debe..ea17463 100644
--- a/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js
+++ b/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -810,7 +810,7 @@
       contextMenu.appendApplicableItems(property.value);
     var copyPathHandler = InspectorFrontendHost.copyText.bind(InspectorFrontendHost, this.nameElement.title);
     contextMenu.beforeShow(() => {
-      contextMenu.appendItem(Common.UIString.capitalize('Copy ^property ^path'), copyPathHandler);
+      contextMenu.appendItem(Common.UIString('Copy property path'), copyPathHandler);
     });
     contextMenu.show();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
index 2f7594a4..a271160 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
@@ -52,7 +52,7 @@
       });
     }
 
-    contextMenu.appendItem(Common.UIString.capitalize('Reveal in Summary ^view'), revealInView.bind(this, 'Summary'));
+    contextMenu.appendItem(Common.UIString('Reveal in Summary view'), revealInView.bind(this, 'Summary'));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js
index 56597f73..ca7db43 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js
@@ -134,7 +134,7 @@
     }
 
     if (node instanceof Profiler.HeapSnapshotRetainingObjectNode)
-      contextMenu.appendItem(Common.UIString.capitalize('Reveal in Summary ^view'), revealInSummaryView.bind(this));
+      contextMenu.appendItem(Common.UIString('Reveal in Summary view'), revealInSummaryView.bind(this));
   }
 
   resetSortingCache() {
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
index 6f7fdec..fa24238 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -57,6 +57,7 @@
     this._applicationTreeElement.appendChild(clearStorageTreeElement);
 
     var storageTreeElement = this._addSidebarSection(Common.UIString('Storage'));
+    this._addRefreshSectionButton(storageTreeElement, this._refreshStorageSection.bind(this));
     this.localStorageListTreeElement =
         new Resources.StorageCategoryTreeElement(panel, Common.UIString('Local Storage'), 'LocalStorage');
     var localStorageIcon = UI.Icon.create('mediumicon-table', 'resource-tree-item');
@@ -83,6 +84,7 @@
     storageTreeElement.appendChild(this.cookieListTreeElement);
 
     var cacheTreeElement = this._addSidebarSection(Common.UIString('Cache'));
+    this._addRefreshSectionButton(cacheTreeElement, this._refreshCacheSection.bind(this));
     this.cacheStorageListTreeElement = new Resources.ServiceWorkerCacheTreeElement(panel);
     cacheTreeElement.appendChild(this.cacheStorageListTreeElement);
     this.applicationCacheListTreeElement =
@@ -131,6 +133,32 @@
   }
 
   /**
+   * @param {!UI.TreeElement} treeElement
+   * @param {function()} refresh
+   */
+  _addRefreshSectionButton(treeElement, refresh) {
+    var refreshIcon = UI.Icon.create('largeicon-refresh', 'sidebar-section-button');
+    refreshIcon.addEventListener('click', refresh, false);
+    treeElement.setTrailingIcons([refreshIcon]);
+  }
+
+  _refreshStorageSection() {
+    var visibleView = this._panel.visibleView;
+    if (visibleView instanceof Resources.DOMStorageItemsView || visibleView instanceof Resources.CookieItemsView) {
+      // Local Storage, Session Storage || Cookies
+      visibleView.refreshItems();
+    } else if (visibleView instanceof Resources.DatabaseTableView) {
+      // Web SQL
+      visibleView.update();
+    }
+    this.indexedDBListTreeElement.refreshIndexedDB();
+  }
+
+  _refreshCacheSection() {
+    this.cacheStorageListTreeElement._refreshCaches();
+  }
+
+  /**
    * @override
    * @param {!SDK.Target} target
    */
@@ -1176,6 +1204,7 @@
     this._idbObjectStoreTreeElements = {};
     var icon = UI.Icon.create('mediumicon-database', 'resource-tree-item');
     this.setLeadingIcons([icon]);
+    this._model.addEventListener(Resources.IndexedDBModel.Events.DatabaseNamesRefreshed, this._refreshIndexedDB, this);
   }
 
   get itemURL() {
@@ -1197,7 +1226,7 @@
   }
 
   _refreshIndexedDB() {
-    this._model.refreshDatabaseNames();
+    this._model.refreshDatabase(this._databaseId);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
index 9b495578..4e75ac2 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
@@ -175,9 +175,10 @@
     this._loadDatabaseNames(databaseId.securityOrigin);
   }
 
-  refreshDatabaseNames() {
+  async refreshDatabaseNames() {
     for (var securityOrigin in this._databaseNamesBySecurityOrigin)
-      this._loadDatabaseNames(securityOrigin);
+      await this._loadDatabaseNames(securityOrigin);
+    this.dispatchEventToListeners(Resources.IndexedDBModel.Events.DatabaseNamesRefreshed);
   }
 
   /**
@@ -414,7 +415,8 @@
 Resources.IndexedDBModel.Events = {
   DatabaseAdded: Symbol('DatabaseAdded'),
   DatabaseRemoved: Symbol('DatabaseRemoved'),
-  DatabaseLoaded: Symbol('DatabaseLoaded')
+  DatabaseLoaded: Symbol('DatabaseLoaded'),
+  DatabaseNamesRefreshed: Symbol('DatabaseNamesRefreshed')
 };
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
index 7380efe5..a782dbb 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
@@ -34,14 +34,15 @@
 Resources.IDBDatabaseView = class extends UI.VBox {
   /**
    * @param {!Resources.IndexedDBModel} model
-   * @param {!Resources.IndexedDBModel.Database} database
+   * @param {?Resources.IndexedDBModel.Database} database
    */
   constructor(model, database) {
     super();
 
     this._model = model;
+    var databaseName = database ? database.databaseId.name : Common.UIString('Loading\u2026');
 
-    this._reportView = new UI.ReportView(database.databaseId.name);
+    this._reportView = new UI.ReportView(databaseName);
     this._reportView.show(this.contentElement);
 
     var bodySection = this._reportView.appendSection('');
@@ -58,7 +59,8 @@
         Common.UIString('Refresh database'));
     footer.appendChild(this._refreshButton);
 
-    this.update(database);
+    if (database)
+      this.update(database);
   }
 
   _refreshDatabase() {
@@ -75,6 +77,7 @@
    */
   update(database) {
     this._database = database;
+    this._reportView.setTitle(this._database.databaseId.name);
     this._refreshDatabase();
     this._updatedForTests();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css b/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
index ccebe240..60783a0 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
+++ b/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
@@ -26,6 +26,21 @@
     display: none;
 }
 
+li.storage-group-list-item
+.trailing-icons {
+    width: 14px;
+    height: 14px;
+    margin-left: 6px;
+}
+
+li.storage-group-list-item
+.trailing-icons
+.sidebar-section-button {
+    position: relative;
+    top: -1px;
+    left: -8px;
+}
+
 .navigator-tree-item {
     margin: -3px -7px -3px -7px;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/source_frame/ImageView.js b/third_party/WebKit/Source/devtools/front_end/source_frame/ImageView.js
index 11e5909..3ffbf9ee 100644
--- a/third_party/WebKit/Source/devtools/front_end/source_frame/ImageView.js
+++ b/third_party/WebKit/Source/devtools/front_end/source_frame/ImageView.js
@@ -108,13 +108,12 @@
   _contextMenu(event) {
     var contextMenu = new UI.ContextMenu(event);
     if (!this._parsedURL.isDataURL())
-      contextMenu.appendItem(Common.UIString.capitalize('Copy ^image URL'), this._copyImageURL.bind(this));
-    if (this._imagePreviewElement.src) {
-      contextMenu.appendItem(
-          Common.UIString.capitalize('Copy ^image as Data URI'), this._copyImageAsDataURL.bind(this));
-    }
-    contextMenu.appendItem(Common.UIString.capitalize('Open ^image in ^new ^tab'), this._openInNewTab.bind(this));
-    contextMenu.appendItem(Common.UIString.capitalize('Save\u2026'), this._saveImage.bind(this));
+      contextMenu.appendItem(Common.UIString('Copy image URL'), this._copyImageURL.bind(this));
+    if (this._imagePreviewElement.src)
+      contextMenu.appendItem(Common.UIString('Copy image as data URI'), this._copyImageAsDataURL.bind(this));
+
+    contextMenu.appendItem(Common.UIString('Open image in new tab'), this._openInNewTab.bind(this));
+    contextMenu.appendItem(Common.UIString('Save\u2026'), this._saveImage.bind(this));
     contextMenu.show();
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js
index ef7578b..504b8d3d 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js
@@ -271,8 +271,8 @@
       return;
     var contextMenu = new UI.ContextMenu(event);
     if (item.debuggerCallFrame)
-      contextMenu.appendItem(Common.UIString.capitalize('Restart ^frame'), () => item.debuggerCallFrame.restart());
-    contextMenu.appendItem(Common.UIString.capitalize('Copy ^stack ^trace'), this._copyStackTrace.bind(this));
+      contextMenu.appendItem(Common.UIString('Restart frame'), () => item.debuggerCallFrame.restart());
+    contextMenu.appendItem(Common.UIString('Copy stack trace'), this._copyStackTrace.bind(this));
     var location = this._itemLocation(item);
     if (location) {
       var uiLocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(location);
@@ -323,22 +323,19 @@
     if (canBlackbox) {
       if (isBlackboxed) {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Stop ^blackboxing'),
-            manager.unblackboxUISourceCode.bind(manager, uiSourceCode));
+            Common.UIString('Stop blackboxing'), manager.unblackboxUISourceCode.bind(manager, uiSourceCode));
       } else {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Blackbox ^script'), manager.blackboxUISourceCode.bind(manager, uiSourceCode));
+            Common.UIString('Blackbox script'), manager.blackboxUISourceCode.bind(manager, uiSourceCode));
       }
     }
     if (isContentScript) {
       if (isBlackboxed) {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Stop blackboxing ^all ^content ^scripts'),
-            manager.blackboxContentScripts.bind(manager));
+            Common.UIString('Stop blackboxing all content scripts'), manager.blackboxContentScripts.bind(manager));
       } else {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Blackbox ^all ^content ^scripts'),
-            manager.unblackboxContentScripts.bind(manager));
+            Common.UIString('Blackbox all content scripts'), manager.unblackboxContentScripts.bind(manager));
       }
     }
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
index 05418f81..27ac557 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
@@ -293,7 +293,7 @@
           !Bindings.blackboxManager.isBlackboxedUISourceCode(this._debuggerSourceCode)) {
         if (this._scriptFileForDebuggerModel.size) {
           var scriptFile = this._scriptFileForDebuggerModel.valuesArray()[0];
-          var addSourceMapURLLabel = Common.UIString.capitalize('Add ^source ^map\u2026');
+          var addSourceMapURLLabel = Common.UIString('Add source map\u2026');
           contextMenu.appendItem(addSourceMapURLLabel, addSourceMapURL.bind(null, scriptFile));
           contextMenu.appendSeparator();
         }
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
index 3861fa4..a7fc4e4 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -779,12 +779,10 @@
       var binding = Persistence.persistence.binding(uiSourceCode);
       if (!binding) {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Map to ^network ^resource\u2026'),
-            this.mapFileSystemToNetwork.bind(this, uiSourceCode));
+            Common.UIString('Map to network resource\u2026'), this.mapFileSystemToNetwork.bind(this, uiSourceCode));
       } else {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Remove ^network ^mapping'),
-            this._removeNetworkMapping.bind(this, binding.network));
+            Common.UIString('Remove network mapping'), this._removeNetworkMapping.bind(this, binding.network));
       }
     }
 
@@ -801,8 +799,7 @@
         return;
       if (this._workspace.uiSourceCodeForURL(uiSourceCode.url()) === uiSourceCode) {
         contextMenu.appendItem(
-            Common.UIString.capitalize('Map to ^file ^system ^resource\u2026'),
-            this.mapNetworkToFileSystem.bind(this, uiSourceCode));
+            Common.UIString('Map to file system resource\u2026'), this.mapNetworkToFileSystem.bind(this, uiSourceCode));
       }
     }
   }
@@ -820,13 +817,13 @@
     if (!uiSourceCode.project().isServiceProject() &&
         !event.target.isSelfOrDescendant(this._navigatorTabbedLocation.widget().element)) {
       contextMenu.appendItem(
-          Common.UIString.capitalize('Reveal in ^navigator'), this._handleContextMenuReveal.bind(this, uiSourceCode));
+          Common.UIString('Reveal in navigator'), this._handleContextMenuReveal.bind(this, uiSourceCode));
       contextMenu.appendSeparator();
     }
     this._appendUISourceCodeMappingItems(contextMenu, uiSourceCode);
     if (!uiSourceCode.project().canSetFileContent()) {
       contextMenu.appendItem(
-          Common.UIString.capitalize('Local ^modifications\u2026'), this._showLocalHistory.bind(this, uiSourceCode));
+          Common.UIString('Local modifications\u2026'), this._showLocalHistory.bind(this, uiSourceCode));
     }
   }
 
@@ -855,10 +852,9 @@
     if (contentType.hasScripts()) {
       var target = UI.context.flavor(SDK.Target);
       var debuggerModel = target ? target.model(SDK.DebuggerModel) : null;
-      if (debuggerModel && debuggerModel.isPaused()) {
-        contextMenu.appendItem(
-            Common.UIString.capitalize('Continue to ^here'), this._continueToLocation.bind(this, uiLocation));
-      }
+      if (debuggerModel && debuggerModel.isPaused())
+        contextMenu.appendItem(Common.UIString('Continue to here'), this._continueToLocation.bind(this, uiLocation));
+
       this._callstackPane.appendBlackboxURLContextMenuItems(contextMenu, uiSourceCode);
     }
   }
@@ -880,11 +876,10 @@
       return;
     var remoteObject = /** @type {!SDK.RemoteObject} */ (target);
     contextMenu.appendItem(
-        Common.UIString.capitalize('Store as ^global ^variable'), this._saveToTempVariable.bind(this, remoteObject));
+        Common.UIString('Store as global variable'), this._saveToTempVariable.bind(this, remoteObject));
     if (remoteObject.type === 'function') {
       contextMenu.appendItem(
-          Common.UIString.capitalize('Show ^function ^definition'),
-          this._showFunctionDefinition.bind(this, remoteObject));
+          Common.UIString('Show function definition'), this._showFunctionDefinition.bind(this, remoteObject));
     }
   }
 
@@ -899,7 +894,7 @@
     var uiSourceCode = this._workspace.uiSourceCodeForURL(request.url());
     if (!uiSourceCode)
       return;
-    var openText = Common.UIString.capitalize('Open in Sources ^panel');
+    var openText = Common.UIString('Open in Sources panel');
     contextMenu.appendItem(openText, this.showUILocation.bind(this, uiSourceCode.uiLocation(0, 0)));
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/WatchExpressionsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/WatchExpressionsSidebarPane.js
index 2d2121b..bc98f10 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/WatchExpressionsSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/WatchExpressionsSidebarPane.js
@@ -156,12 +156,11 @@
       isEditing |= watchExpression.isEditing();
 
     if (!isEditing)
-      contextMenu.appendItem(Common.UIString.capitalize('Add ^watch ^expression'), this._addButtonClicked.bind(this));
+      contextMenu.appendItem(Common.UIString('Add watch expression'), this._addButtonClicked.bind(this));
 
-    if (this._watchExpressions.length > 1) {
-      contextMenu.appendItem(
-          Common.UIString.capitalize('Delete ^all ^watch ^expressions'), this._deleteAllButtonClicked.bind(this));
-    }
+    if (this._watchExpressions.length > 1)
+      contextMenu.appendItem(Common.UIString('Delete all watch expressions'), this._deleteAllButtonClicked.bind(this));
+
 
     var target = event.deepElementFromPoint();
     if (!target)
@@ -405,13 +404,12 @@
    * @param {!Event} event
    */
   _populateContextMenu(contextMenu, event) {
-    if (!this.isEditing()) {
-      contextMenu.appendItem(
-          Common.UIString.capitalize('Delete ^watch ^expression'), this._updateExpression.bind(this, null));
-    }
+    if (!this.isEditing())
+      contextMenu.appendItem(Common.UIString('Delete watch expression'), this._updateExpression.bind(this, null));
+
 
     if (!this.isEditing() && this._result && (this._result.type === 'number' || this._result.type === 'string'))
-      contextMenu.appendItem(Common.UIString.capitalize('Copy ^value'), this._copyValueButtonClicked.bind(this));
+      contextMenu.appendItem(Common.UIString('Copy value'), this._copyValueButtonClicked.bind(this));
 
     var target = event.deepElementFromPoint();
     if (target && this._valueElement.isSelfOrAncestor(target))
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/XHRBreakpointsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/XHRBreakpointsSidebarPane.js
index c4bdfe8..ba70f808 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/XHRBreakpointsSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/XHRBreakpointsSidebarPane.js
@@ -36,7 +36,7 @@
 
   _emptyElementContextMenu(event) {
     var contextMenu = new UI.ContextMenu(event);
-    contextMenu.appendItem(Common.UIString.capitalize('Add ^breakpoint'), this._addButtonClicked.bind(this));
+    contextMenu.appendItem(Common.UIString('Add breakpoint'), this._addButtonClicked.bind(this));
     contextMenu.show();
   }
 
@@ -153,10 +153,10 @@
         this._removeBreakpoint(url);
       }
     }
-    var removeAllTitle = Common.UIString.capitalize('Remove ^all ^breakpoints');
+    var removeAllTitle = Common.UIString('Remove all breakpoints');
 
-    contextMenu.appendItem(Common.UIString.capitalize('Add ^breakpoint'), this._addButtonClicked.bind(this));
-    contextMenu.appendItem(Common.UIString.capitalize('Remove ^breakpoint'), removeBreakpoint.bind(this));
+    contextMenu.appendItem(Common.UIString('Add breakpoint'), this._addButtonClicked.bind(this));
+    contextMenu.appendItem(Common.UIString('Remove breakpoint'), removeBreakpoint.bind(this));
     contextMenu.appendItem(removeAllTitle, removeAllBreakpoints.bind(this));
     contextMenu.show();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ReportView.js b/third_party/WebKit/Source/devtools/front_end/ui/ReportView.js
index bd08064..16b6346 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/ReportView.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/ReportView.js
@@ -14,12 +14,22 @@
 
     var contentBox = this.contentElement.createChild('div', 'report-content-box');
     this._headerElement = contentBox.createChild('div', 'report-header vbox');
-    this._headerElement.createChild('div', 'report-title').textContent = title;
+    this._titleElement = this._headerElement.createChild('div', 'report-title');
+    this._titleElement.textContent = title;
 
     this._sectionList = contentBox.createChild('div', 'vbox');
   }
 
   /**
+   * @param {string} title
+   */
+  setTitle(title) {
+    if (this._titleElement && this._titleElement.textContent === title)
+      return;
+    this._titleElement.textContent = title;
+  }
+
+  /**
    * @param {string} subtitle
    */
   setSubtitle(subtitle) {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
index f237768..db8eb31 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
@@ -1185,10 +1185,10 @@
 
     var contextMenu = new UI.ContextMenu(event);
     if (this._closeable) {
-      contextMenu.appendItem(Common.UIString.capitalize('Close'), close.bind(this));
-      contextMenu.appendItem(Common.UIString.capitalize('Close ^others'), closeOthers.bind(this));
-      contextMenu.appendItem(Common.UIString.capitalize('Close ^tabs to the ^right'), closeToTheRight.bind(this));
-      contextMenu.appendItem(Common.UIString.capitalize('Close ^all'), closeAll.bind(this));
+      contextMenu.appendItem(Common.UIString('Close'), close.bind(this));
+      contextMenu.appendItem(Common.UIString('Close others'), closeOthers.bind(this));
+      contextMenu.appendItem(Common.UIString('Close tabs to the right'), closeToTheRight.bind(this));
+      contextMenu.appendItem(Common.UIString('Close all'), closeAll.bind(this));
     }
     if (this._delegate)
       this._delegate.onContextMenu(this.id, contextMenu);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
index f165b66..6f458e91 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -630,14 +630,14 @@
  * @return {string}
  */
 UI.openLinkExternallyLabel = function() {
-  return Common.UIString.capitalize('Open in ^new ^tab');
+  return Common.UIString('Open in new tab');
 };
 
 /**
  * @return {string}
  */
 UI.copyLinkAddressLabel = function() {
-  return Common.UIString.capitalize('Copy ^link ^address');
+  return Common.UIString('Copy link address');
 };
 
 /**
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
index 56255791..a7b5b033 100644
--- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -1831,9 +1831,8 @@
 
     if (is_source_over &&
         image_type == CanvasRenderingContext2DState::kNoImage) {
-      PaintShader* shader = flags->getShader();
-      if (shader) {
-        if (shader->isOpaque() && alpha == 0xFF)
+      if (flags->HasShader()) {
+        if (flags->ShaderIsOpaque() && alpha == 0xFF)
           GetImageBuffer()->WillOverwriteCanvas();
         return;
       }
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index 1a53ff5..0bc75ca1 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -55,17 +55,25 @@
 VRDisplay::VRDisplay(NavigatorVR* navigator_vr,
                      device::mojom::blink::VRDisplayPtr display,
                      device::mojom::blink::VRDisplayClientRequest request)
-    : ContextLifecycleObserver(navigator_vr->GetDocument()),
+    : SuspendableObject(navigator_vr->GetDocument()),
       navigator_vr_(navigator_vr),
       capabilities_(new VRDisplayCapabilities()),
       eye_parameters_left_(new VREyeParameters()),
       eye_parameters_right_(new VREyeParameters()),
       display_(std::move(display)),
       submit_frame_client_binding_(this),
-      display_client_binding_(this, std::move(request)) {}
+      display_client_binding_(this, std::move(request)) {
+  SuspendIfNeeded();  // Initialize SuspendabaleObject.
+}
 
 VRDisplay::~VRDisplay() {}
 
+void VRDisplay::Suspend() {}
+
+void VRDisplay::Resume() {
+  RequestVSync();
+}
+
 VRController* VRDisplay::Controller() {
   return navigator_vr_->Controller();
 }
@@ -135,6 +143,9 @@
            << " in_animation_frame_=" << in_animation_frame_
            << " did_submit_this_frame_=" << did_submit_this_frame_;
 
+  if (!pending_vrdisplay_raf_)
+    return;
+
   // The logic here is a bit subtle. We get called from one of the following
   // four contexts:
   //
@@ -802,6 +813,12 @@
     return;
   }
 
+  if (doc->IsContextSuspended()) {
+    // We are currently suspended - try ProcessScheduledAnimations again later
+    // when we resume.
+    return;
+  }
+
   TRACE_EVENT1("gpu", "VRDisplay::OnVSync", "frame", vr_frame_id_);
 
   if (pending_vrdisplay_raf_ && scripted_animation_controller_) {
@@ -911,7 +928,8 @@
   return EventTargetNames::VRDisplay;
 }
 
-void VRDisplay::ContextDestroyed(ExecutionContext*) {
+void VRDisplay::ContextDestroyed(ExecutionContext* context) {
+  SuspendableObject::ContextDestroyed(context);
   ForceExitPresent();
   scripted_animation_controller_.Clear();
 }
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.h b/third_party/WebKit/Source/modules/vr/VRDisplay.h
index 308ed3f..87ceae0 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.h
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.h
@@ -7,6 +7,7 @@
 
 #include "core/dom/Document.h"
 #include "core/dom/FrameRequestCallback.h"
+#include "core/dom/SuspendableObject.h"
 #include "core/events/EventTarget.h"
 #include "device/vr/vr_service.mojom-blink.h"
 #include "modules/vr/VRDisplayCapabilities.h"
@@ -39,7 +40,7 @@
 
 class VRDisplay final : public EventTargetWithInlineData,
                         public ActiveScriptWrappable<VRDisplay>,
-                        public ContextLifecycleObserver,
+                        public SuspendableObject,
                         public device::mojom::blink::VRDisplayClient,
                         public device::mojom::blink::VRSubmitFrameClient {
   DEFINE_WRAPPERTYPEINFO();
@@ -89,6 +90,10 @@
   // ScriptWrappable implementation.
   bool HasPendingActivity() const final;
 
+  // SuspendableObject:
+  void Suspend() override;
+  void Resume() override;
+
   void FocusChanged();
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
index 6bdb579e..69b3132 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
@@ -95,6 +95,7 @@
       destination_node_(nullptr),
       is_cleared_(false),
       is_resolving_resume_promises_(false),
+      has_posted_cleanup_task_(false),
       user_gesture_required_(false),
       connection_count_(0),
       deferred_task_handler_(DeferredTaskHandler::Create()),
@@ -149,7 +150,6 @@
   // be in the destructor if there are still AudioNodes around.
   DCHECK(!IsDestinationInitialized());
   DCHECK(!active_source_nodes_.size());
-  DCHECK(!finished_source_handlers_.size());
   DCHECK(!is_resolving_resume_promises_);
   DCHECK(!resume_resolvers_.size());
   DCHECK(!autoplay_status_.has_value());
@@ -700,34 +700,10 @@
 void BaseAudioContext::NotifySourceNodeFinishedProcessing(
     AudioHandler* handler) {
   DCHECK(IsAudioThread());
+  MutexLocker lock(finished_source_handlers_mutex_);
   finished_source_handlers_.push_back(handler);
 }
 
-void BaseAudioContext::RemoveFinishedSourceNodes(bool needs_removal) {
-  DCHECK(IsAudioThread());
-
-  if (needs_removal) {
-    Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
-        BLINK_FROM_HERE,
-        CrossThreadBind(
-            &BaseAudioContext::RemoveFinishedSourceNodesOnMainThread,
-            WrapCrossThreadPersistent(this)));
-  }
-}
-
-void BaseAudioContext::RemoveFinishedSourceNodesOnMainThread() {
-  DCHECK(IsMainThread());
-  AutoLocker locker(this);
-  // Quadratic worst case, but sizes of both vectors are considered
-  // manageable, especially |m_finishedSourceNodes| is likely to be short.
-  for (AudioNode* node : finished_source_nodes_) {
-    size_t i = active_source_nodes_.Find(node);
-    if (i != kNotFound)
-      active_source_nodes_.erase(i);
-  }
-  finished_source_nodes_.clear();
-}
-
 Document* BaseAudioContext::GetDocument() const {
   return ToDocument(GetExecutionContext());
 }
@@ -754,26 +730,6 @@
   return false;
 }
 
-bool BaseAudioContext::ReleaseFinishedSourceNodes() {
-  DCHECK(IsGraphOwner());
-  DCHECK(IsAudioThread());
-  bool did_remove = false;
-  for (AudioHandler* handler : finished_source_handlers_) {
-    for (AudioNode* node : active_source_nodes_) {
-      if (finished_source_nodes_.Contains(node))
-        continue;
-      if (handler == &node->Handler()) {
-        handler->BreakConnection();
-        finished_source_nodes_.insert(node);
-        did_remove = true;
-        break;
-      }
-    }
-  }
-  finished_source_handlers_.clear();
-  return did_remove;
-}
-
 void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) {
   DCHECK(IsMainThread());
   AutoLocker locker(this);
@@ -791,23 +747,11 @@
 }
 
 void BaseAudioContext::HandleStoppableSourceNodes() {
+  DCHECK(IsAudioThread());
   DCHECK(IsGraphOwner());
 
-  // Find AudioBufferSourceNodes to see if we can stop playing them.
-  for (AudioNode* node : active_source_nodes_) {
-    // If the AudioNode has been marked as finished and released by
-    // the audio thread, but not yet removed by the main thread
-    // (see releaseActiveSourceNodes() above), |node| must not be
-    // touched as its handler may have been released already.
-    if (finished_source_nodes_.Contains(node))
-      continue;
-    if (node->Handler().GetNodeType() ==
-        AudioHandler::kNodeTypeAudioBufferSource) {
-      AudioBufferSourceNode* source_node =
-          static_cast<AudioBufferSourceNode*>(node);
-      source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
-    }
-  }
+  if (active_source_nodes_.size())
+    ScheduleMainThreadCleanup();
 }
 
 void BaseAudioContext::HandlePreRenderTasks(
@@ -844,39 +788,88 @@
   // is that there will be some nodes which will take slightly longer than usual
   // to be deleted or removed from the render graph (in which case they'll
   // render silence).
-  bool did_remove = false;
   if (TryLock()) {
     // Take care of AudioNode tasks where the tryLock() failed previously.
     GetDeferredTaskHandler().BreakConnections();
 
-    // Dynamically clean up nodes which are no longer needed.
-    did_remove = ReleaseFinishedSourceNodes();
-
     GetDeferredTaskHandler().HandleDeferredTasks();
     GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread();
 
     unlock();
   }
-
-  RemoveFinishedSourceNodes(did_remove);
 }
 
-void BaseAudioContext::ResolvePromisesForResumeOnMainThread() {
+void BaseAudioContext::PerformCleanupOnMainThread() {
   DCHECK(IsMainThread());
   AutoLocker locker(this);
 
-  for (auto& resolver : resume_resolvers_) {
-    if (context_state_ == kClosed) {
-      resolver->Reject(DOMException::Create(
-          kInvalidStateError, "Cannot resume a context that has been closed"));
-    } else {
-      SetContextState(kRunning);
-      resolver->Resolve();
+  if (is_resolving_resume_promises_) {
+    for (auto& resolver : resume_resolvers_) {
+      if (context_state_ == kClosed) {
+        resolver->Reject(DOMException::Create(
+            kInvalidStateError,
+            "Cannot resume a context that has been closed"));
+      } else {
+        SetContextState(kRunning);
+        resolver->Resolve();
+      }
     }
+    resume_resolvers_.clear();
+    is_resolving_resume_promises_ = false;
   }
 
-  resume_resolvers_.clear();
-  is_resolving_resume_promises_ = false;
+  if (active_source_nodes_.size()) {
+    // Find AudioBufferSourceNodes to see if we can stop playing them.
+    for (AudioNode* node : active_source_nodes_) {
+      if (node->Handler().GetNodeType() ==
+          AudioHandler::kNodeTypeAudioBufferSource) {
+        AudioBufferSourceNode* source_node =
+            static_cast<AudioBufferSourceNode*>(node);
+        source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
+      }
+    }
+
+    Vector<AudioHandler*> finished_handlers;
+    {
+      MutexLocker lock(finished_source_handlers_mutex_);
+      finished_source_handlers_.swap(finished_handlers);
+    }
+    // Break the connection and release active nodes that have finished
+    // playing.
+    unsigned remove_count = 0;
+    Vector<bool> removables;
+    removables.resize(active_source_nodes_.size());
+    for (AudioHandler* handler : finished_handlers) {
+      for (unsigned i = 0; i < active_source_nodes_.size(); ++i) {
+        if (handler == &active_source_nodes_[i]->Handler()) {
+          handler->BreakConnection();
+          removables[i] = true;
+          remove_count++;
+          break;
+        }
+      }
+    }
+
+    // Copy over the surviving active nodes.
+    HeapVector<Member<AudioNode>> actives;
+    actives.ReserveInitialCapacity(active_source_nodes_.size() - remove_count);
+    for (unsigned i = 0; i < removables.size(); ++i) {
+      if (!removables[i])
+        actives.push_back(active_source_nodes_[i]);
+    }
+    active_source_nodes_.swap(actives);
+  }
+  has_posted_cleanup_task_ = false;
+}
+
+void BaseAudioContext::ScheduleMainThreadCleanup() {
+  if (has_posted_cleanup_task_)
+    return;
+  Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
+      BLINK_FROM_HERE,
+      CrossThreadBind(&BaseAudioContext::PerformCleanupOnMainThread,
+                      WrapCrossThreadPersistent(this)));
+  has_posted_cleanup_task_ = true;
 }
 
 void BaseAudioContext::ResolvePromisesForResume() {
@@ -890,10 +883,7 @@
   // often and it takes some time to resolve the promises in the main thread.
   if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) {
     is_resolving_resume_promises_ = true;
-    Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
-        BLINK_FROM_HERE,
-        CrossThreadBind(&BaseAudioContext::ResolvePromisesForResumeOnMainThread,
-                        WrapCrossThreadPersistent(this)));
+    ScheduleMainThreadCleanup();
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index 13db3f7..20852989 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -273,16 +273,6 @@
   // Called at the end of each render quantum.
   void HandlePostRenderTasks();
 
-  // Called periodically at the end of each render quantum to release
-  // finished source nodes.  Updates m_finishedSourceNodes with nodes
-  // to be deleted.  Returns true if any node needs deletion.  Must be
-  // run from the audio thread.
-  bool ReleaseFinishedSourceNodes();
-
-  // The finished source nodes found by |releaseFinishedSourceNodes|
-  // will be removed on the main thread, which is done here.
-  void RemoveFinishedSourceNodes(bool needs_removal);
-
   // Keeps track of the number of connections made.
   void IncrementConnectionCount() {
     DCHECK(IsMainThread());
@@ -413,11 +403,6 @@
   // haven't finished playing.  Make sure to release them here.
   void ReleaseActiveSourceNodes();
 
-  // Actually remove the nodes noted for deletion by
-  // releaseFinishedSourceNodes.  Must be run from the main thread,
-  // and must not be run with the context lock.
-  void RemoveFinishedSourceNodesOnMainThread();
-
   // Returns the Document wich wich the instance is associated.
   Document* GetDocument() const;
 
@@ -430,9 +415,12 @@
   // Listener for the PannerNodes
   Member<AudioListener> listener_;
 
-  // Only accessed in the audio thread.
+  // Accessed by audio thread and main thread, coordinated using
+  // the associated mutex.
+  //
   // These raw pointers are safe because AudioSourceNodes in
-  // m_activeSourceNodes own them.
+  // active_source_nodes_ own them.
+  Mutex finished_source_handlers_mutex_;
   Vector<AudioHandler*> finished_source_handlers_;
 
   // List of source nodes. This is either accessed when the graph lock is
@@ -443,17 +431,24 @@
   // this.
   HeapVector<Member<AudioNode>> active_source_nodes_;
 
-  // The main thread controls m_activeSourceNodes, all updates and additions
-  // are performed by it. When the audio thread marks a source node as finished,
-  // the nodes are added to |m_finishedSourceNodes| and scheduled for removal
-  // from |m_activeSourceNodes| by the main thread.
-  HashSet<UntracedMember<AudioNode>> finished_source_nodes_;
-
-  // FIXME(dominicc): Move these to AudioContext because only
-  // it creates these Promises.
-  // Handle Promises for resume() and suspend()
+  // Called by the audio thread to handle Promises for resume() and suspend(),
+  // posting a main thread task to perform the actual resolving, if needed.
+  //
+  // TODO(dominicc): Move to AudioContext because only it creates
+  // these Promises.
   void ResolvePromisesForResume();
-  void ResolvePromisesForResumeOnMainThread();
+
+  // The audio thread relies on the main thread to perform some operations
+  // over the objects that it owns and controls; |ScheduleMainThreadCleanup()|
+  // posts the task to initiate those.
+  //
+  // That is, we combine all those sub-tasks into one task action for
+  // convenience and performance, |PerformCleanupOnMainThread()|. It handles
+  // promise resolving, stopping and finishing up of audio source nodes etc.
+  // Actions that should happen, but can happen asynchronously to the
+  // audio thread making rendering progress.
+  void ScheduleMainThreadCleanup();
+  void PerformCleanupOnMainThread();
 
   // When the context is going away, reject any pending script promise
   // resolvers.
@@ -467,6 +462,11 @@
   // don't want to call resolve an excessive number of times.
   bool is_resolving_resume_promises_;
 
+  // Set to |true| by the audio thread when it posts a main-thread task to
+  // perform delayed state sync'ing updates that needs to be done on the main
+  // thread. Cleared by the main thread task once it has run.
+  bool has_posted_cleanup_task_;
+
   // Whether a user gesture is required to start this AudioContext.
   bool user_gesture_required_;
 
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/OfflineAudioContext.cpp
index b7dcfba5..257fc90 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioContext.cpp
@@ -388,17 +388,13 @@
 
   // OfflineGraphAutoLocker here locks the audio graph for the same reason
   // above in |handlePreOfflineRenderTasks|.
-  bool did_remove = false;
   {
     OfflineGraphAutoLocker locker(this);
 
     GetDeferredTaskHandler().BreakConnections();
-    did_remove = ReleaseFinishedSourceNodes();
     GetDeferredTaskHandler().HandleDeferredTasks();
     GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread();
   }
-
-  RemoveFinishedSourceNodes(did_remove);
 }
 
 OfflineAudioDestinationHandler& OfflineAudioContext::DestinationHandler() {
diff --git a/third_party/WebKit/Source/platform/graphics/Gradient.cpp b/third_party/WebKit/Source/platform/graphics/Gradient.cpp
index a96f9f7..af08ed4 100644
--- a/third_party/WebKit/Source/platform/graphics/Gradient.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Gradient.cpp
@@ -121,7 +121,7 @@
   }
 }
 
-sk_sp<PaintShader> Gradient::CreateShaderInternal(
+std::unique_ptr<PaintShader> Gradient::CreateShaderInternal(
     const SkMatrix& local_matrix) {
   SortStopsIfNecessary();
   DCHECK(stops_sorted_);
@@ -151,20 +151,20 @@
   uint32_t flags = color_interpolation_ == ColorInterpolation::kPremultiplied
                        ? SkGradientShader::kInterpolateColorsInPremul_Flag
                        : 0;
-  sk_sp<SkShader> shader = CreateShader(colors, pos, tile, flags, local_matrix);
-  if (!shader) {
-    // use last color, since our "geometry" was degenerate (e.g. radius==0)
-    shader = SkShader::MakeColorShader(colors.back());
-  }
+  std::unique_ptr<PaintShader> shader =
+      CreateShader(colors, pos, tile, flags, local_matrix, colors.back());
+  DCHECK(shader);
 
-  return WrapSkShader(std::move(shader));
+  return shader;
 }
 
 void Gradient::ApplyToFlags(PaintFlags& flags, const SkMatrix& local_matrix) {
-  if (!cached_shader_ || local_matrix != cached_shader_->getLocalMatrix())
+  if (!cached_shader_ ||
+      local_matrix != cached_shader_->sk_shader()->getLocalMatrix()) {
     cached_shader_ = CreateShaderInternal(local_matrix);
+  }
 
-  flags.setShader(cached_shader_);
+  flags.setShader(WTF::MakeUnique<PaintShader>(*cached_shader_));
 
   // Legacy behavior: gradients are always dithered.
   flags.setDither(true);
@@ -183,15 +183,17 @@
         p1_(p1) {}
 
  protected:
-  sk_sp<SkShader> CreateShader(const ColorBuffer& colors,
-                               const OffsetBuffer& pos,
-                               SkShader::TileMode tile_mode,
-                               uint32_t flags,
-                               const SkMatrix& local_matrix) const override {
+  std::unique_ptr<PaintShader> CreateShader(
+      const ColorBuffer& colors,
+      const OffsetBuffer& pos,
+      SkShader::TileMode tile_mode,
+      uint32_t flags,
+      const SkMatrix& local_matrix,
+      SkColor fallback_color) const override {
     SkPoint pts[2] = {p0_.Data(), p1_.Data()};
-    return SkGradientShader::MakeLinear(pts, colors.data(), pos.data(),
-                                        static_cast<int>(colors.size()),
-                                        tile_mode, flags, &local_matrix);
+    return PaintShader::MakeLinearGradient(
+        pts, colors.data(), pos.data(), static_cast<int>(colors.size()),
+        tile_mode, flags, &local_matrix, fallback_color);
   }
 
  private:
@@ -216,11 +218,13 @@
         aspect_ratio_(aspect_ratio) {}
 
  protected:
-  sk_sp<SkShader> CreateShader(const ColorBuffer& colors,
-                               const OffsetBuffer& pos,
-                               SkShader::TileMode tile_mode,
-                               uint32_t flags,
-                               const SkMatrix& local_matrix) const override {
+  std::unique_ptr<PaintShader> CreateShader(
+      const ColorBuffer& colors,
+      const OffsetBuffer& pos,
+      SkShader::TileMode tile_mode,
+      uint32_t flags,
+      const SkMatrix& local_matrix,
+      SkColor fallback_color) const override {
     SkTCopyOnFirstWrite<SkMatrix> adjusted_local_matrix(local_matrix);
     if (aspect_ratio_ != 1) {
       // CSS3 elliptical gradients: apply the elliptical scaling at the
@@ -233,20 +237,20 @@
     // Since the two-point radial gradient is slower than the plain radial,
     // only use it if we have to.
     if (p0_ == p1_ && r0_ <= 0.0f) {
-      return SkGradientShader::MakeRadial(
+      return PaintShader::MakeRadialGradient(
           p1_.Data(), r1_, colors.data(), pos.data(),
           static_cast<int>(colors.size()), tile_mode, flags,
-          adjusted_local_matrix);
+          adjusted_local_matrix, fallback_color);
     }
 
     // The radii we give to Skia must be positive. If we're given a
     // negative radius, ask for zero instead.
     const SkScalar radius0 = std::max(WebCoreFloatToSkScalar(r0_), 0.0f);
     const SkScalar radius1 = std::max(WebCoreFloatToSkScalar(r1_), 0.0f);
-    return SkGradientShader::MakeTwoPointConical(
+    return PaintShader::MakeTwoPointConicalGradient(
         p0_.Data(), radius0, p1_.Data(), radius1, colors.data(), pos.data(),
         static_cast<int>(colors.size()), tile_mode, flags,
-        adjusted_local_matrix);
+        adjusted_local_matrix, fallback_color);
   }
 
  private:
@@ -267,11 +271,13 @@
         angle_(angle) {}
 
  protected:
-  sk_sp<SkShader> CreateShader(const ColorBuffer& colors,
-                               const OffsetBuffer& pos,
-                               SkShader::TileMode tile_mode,
-                               uint32_t flags,
-                               const SkMatrix& local_matrix) const override {
+  std::unique_ptr<PaintShader> CreateShader(
+      const ColorBuffer& colors,
+      const OffsetBuffer& pos,
+      SkShader::TileMode tile_mode,
+      uint32_t flags,
+      const SkMatrix& local_matrix,
+      SkColor fallback_color) const override {
     DCHECK_NE(tile_mode, SkShader::kMirror_TileMode);
 
     // Skia's sweep gradient angles are relative to the x-axis, not the y-axis.
@@ -282,9 +288,10 @@
                                                   position_.Y());
     }
 
-    return SkGradientShader::MakeSweep(
+    return PaintShader::MakeSweepGradient(
         position_.X(), position_.Y(), colors.data(), pos.data(),
-        static_cast<int>(colors.size()), flags, adjusted_local_matrix);
+        static_cast<int>(colors.size()), flags, adjusted_local_matrix,
+        fallback_color);
   }
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/Gradient.h b/third_party/WebKit/Source/platform/graphics/Gradient.h
index 8eb54c1..b7a1c6a5 100644
--- a/third_party/WebKit/Source/platform/graphics/Gradient.h
+++ b/third_party/WebKit/Source/platform/graphics/Gradient.h
@@ -102,14 +102,16 @@
 
   using ColorBuffer = Vector<SkColor, 8>;
   using OffsetBuffer = Vector<SkScalar, 8>;
-  virtual sk_sp<SkShader> CreateShader(const ColorBuffer&,
-                                       const OffsetBuffer&,
-                                       SkShader::TileMode,
-                                       uint32_t flags,
-                                       const SkMatrix&) const = 0;
+  virtual std::unique_ptr<PaintShader> CreateShader(const ColorBuffer&,
+                                                    const OffsetBuffer&,
+                                                    SkShader::TileMode,
+                                                    uint32_t flags,
+                                                    const SkMatrix&,
+                                                    SkColor) const = 0;
 
  private:
-  sk_sp<PaintShader> CreateShaderInternal(const SkMatrix& local_matrix);
+  std::unique_ptr<PaintShader> CreateShaderInternal(
+      const SkMatrix& local_matrix);
 
   void SortStopsIfNecessary();
   void FillSkiaStops(ColorBuffer&, OffsetBuffer&) const;
@@ -121,7 +123,7 @@
   Vector<ColorStop, 2> stops_;
   bool stops_sorted_;
 
-  mutable sk_sp<PaintShader> cached_shader_;
+  mutable std::unique_ptr<PaintShader> cached_shader_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/Image.cpp b/third_party/WebKit/Source/platform/graphics/Image.cpp
index c0fe493d..d930750 100644
--- a/third_party/WebKit/Source/platform/graphics/Image.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Image.cpp
@@ -227,14 +227,15 @@
 
 namespace {
 
-sk_sp<PaintShader> CreatePatternShader(const PaintImage& image,
-                                       const SkMatrix& shader_matrix,
-                                       const PaintFlags& paint,
-                                       const FloatSize& spacing,
-                                       SkShader::TileMode tmx,
-                                       SkShader::TileMode tmy) {
-  if (spacing.IsZero())
-    return MakePaintShaderImage(image.sk_image(), tmx, tmy, &shader_matrix);
+std::unique_ptr<PaintShader> CreatePatternShader(const PaintImage& image,
+                                                 const SkMatrix& shader_matrix,
+                                                 const PaintFlags& paint,
+                                                 const FloatSize& spacing,
+                                                 SkShader::TileMode tmx,
+                                                 SkShader::TileMode tmy) {
+  if (spacing.IsZero()) {
+    return PaintShader::MakeImage(image.sk_image(), tmx, tmy, &shader_matrix);
+  }
 
   // Arbitrary tiling is currently only supported for SkPictureShader, so we use
   // that instead of a plain bitmap shader to implement spacing.
@@ -246,8 +247,8 @@
   PaintCanvas* canvas = recorder.beginRecording(tile_rect);
   canvas->drawImage(image, 0, 0, &paint);
 
-  return MakePaintShaderRecord(recorder.finishRecordingAsPicture(), tile_rect,
-                               tmx, tmy, &shader_matrix);
+  return PaintShader::MakePaintRecord(recorder.finishRecordingAsPicture(),
+                                      tile_rect, tmx, tmy, &shader_matrix);
 }
 
 SkShader::TileMode ComputeTileMode(float left,
@@ -327,7 +328,7 @@
   // If the shader could not be instantiated (e.g. non-invertible matrix),
   // draw transparent.
   // Note: we can't simply bail, because of arbitrary blend mode.
-  if (!flags.getShader())
+  if (!flags.HasShader())
     flags.setColor(SK_ColorTRANSPARENT);
 
   context.DrawRect(dest_rect, flags);
@@ -359,9 +360,10 @@
   if (!image)
     return false;
 
-  flags.setShader(image->makeShader(SkShader::kRepeat_TileMode,
-                                    SkShader::kRepeat_TileMode, &local_matrix));
-  if (!flags.getShader())
+  flags.setShader(
+      PaintShader::MakeImage(std::move(image), SkShader::kRepeat_TileMode,
+                             SkShader::kRepeat_TileMode, &local_matrix));
+  if (!flags.HasShader())
     return false;
 
   // Animation is normally refreshed in draw() impls, which we don't call when
diff --git a/third_party/WebKit/Source/platform/graphics/ImagePattern.cpp b/third_party/WebKit/Source/platform/graphics/ImagePattern.cpp
index b913790..dbf9de5 100644
--- a/third_party/WebKit/Source/platform/graphics/ImagePattern.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImagePattern.cpp
@@ -29,14 +29,16 @@
   return local_matrix != previous_local_matrix_;
 }
 
-sk_sp<PaintShader> ImagePattern::CreateShader(const SkMatrix& local_matrix) {
-  if (!tile_image_)
-    return WrapSkShader(SkShader::MakeColorShader(SK_ColorTRANSPARENT));
+std::unique_ptr<PaintShader> ImagePattern::CreateShader(
+    const SkMatrix& local_matrix) {
+  if (!tile_image_) {
+    return PaintShader::MakeColor(SK_ColorTRANSPARENT);
+  }
 
   if (IsRepeatXY()) {
     // Fast path: for repeatXY we just return a shader from the original image.
-    return MakePaintShaderImage(tile_image_, SkShader::kRepeat_TileMode,
-                                SkShader::kRepeat_TileMode, &local_matrix);
+    return PaintShader::MakeImage(tile_image_, SkShader::kRepeat_TileMode,
+                                  SkShader::kRepeat_TileMode, &local_matrix);
   }
 
   // Skia does not have a "draw the tile only once" option. Clamp_TileMode
@@ -73,8 +75,8 @@
   SkMatrix adjusted_matrix(local_matrix);
   adjusted_matrix.postTranslate(-border_pixel_x, -border_pixel_y);
 
-  return MakePaintShaderImage(std::move(tile_image), tile_mode_x, tile_mode_y,
-                              &adjusted_matrix);
+  return PaintShader::MakeImage(std::move(tile_image), tile_mode_x, tile_mode_y,
+                                &adjusted_matrix);
 }
 
 bool ImagePattern::IsTextureBacked() const {
diff --git a/third_party/WebKit/Source/platform/graphics/ImagePattern.h b/third_party/WebKit/Source/platform/graphics/ImagePattern.h
index a9c0ffb..ed52189 100644
--- a/third_party/WebKit/Source/platform/graphics/ImagePattern.h
+++ b/third_party/WebKit/Source/platform/graphics/ImagePattern.h
@@ -20,7 +20,7 @@
   bool IsTextureBacked() const override;
 
  protected:
-  sk_sp<PaintShader> CreateShader(const SkMatrix&) override;
+  std::unique_ptr<PaintShader> CreateShader(const SkMatrix&) override;
   bool IsLocalMatrixChanged(const SkMatrix&) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.cpp b/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.cpp
index 1a9250f..c3b59885 100644
--- a/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.cpp
+++ b/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.cpp
@@ -33,11 +33,11 @@
 
 PaintRecordPattern::~PaintRecordPattern() {}
 
-sk_sp<PaintShader> PaintRecordPattern::CreateShader(
+std::unique_ptr<PaintShader> PaintRecordPattern::CreateShader(
     const SkMatrix& local_matrix) {
-  return MakePaintShaderRecord(tile_record_, tile_record_bounds_,
-                               SkShader::kRepeat_TileMode,
-                               SkShader::kRepeat_TileMode, &local_matrix);
+  return PaintShader::MakePaintRecord(
+      tile_record_, tile_record_bounds_, SkShader::kRepeat_TileMode,
+      SkShader::kRepeat_TileMode, &local_matrix);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.h b/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.h
index fe3d95d..3d826a63 100644
--- a/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.h
+++ b/third_party/WebKit/Source/platform/graphics/PaintRecordPattern.h
@@ -21,7 +21,7 @@
   ~PaintRecordPattern() override;
 
  protected:
-  sk_sp<PaintShader> CreateShader(const SkMatrix&) override;
+  std::unique_ptr<PaintShader> CreateShader(const SkMatrix&) override;
 
  private:
   PaintRecordPattern(sk_sp<PaintRecord>,
diff --git a/third_party/WebKit/Source/platform/graphics/Pattern.cpp b/third_party/WebKit/Source/platform/graphics/Pattern.cpp
index dc1c2f6..53cf3a8 100644
--- a/third_party/WebKit/Source/platform/graphics/Pattern.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Pattern.cpp
@@ -59,11 +59,11 @@
   if (!cached_shader_ || IsLocalMatrixChanged(local_matrix))
     cached_shader_ = CreateShader(local_matrix);
 
-  flags.setShader(cached_shader_);
+  flags.setShader(WTF::MakeUnique<PaintShader>(*cached_shader_));
 }
 
 bool Pattern::IsLocalMatrixChanged(const SkMatrix& local_matrix) const {
-  return local_matrix != cached_shader_->getLocalMatrix();
+  return local_matrix != cached_shader_->sk_shader()->getLocalMatrix();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/Pattern.h b/third_party/WebKit/Source/platform/graphics/Pattern.h
index 1db8d22..f939b5e 100644
--- a/third_party/WebKit/Source/platform/graphics/Pattern.h
+++ b/third_party/WebKit/Source/platform/graphics/Pattern.h
@@ -72,13 +72,13 @@
   virtual bool IsTextureBacked() const { return false; }
 
  protected:
-  virtual sk_sp<PaintShader> CreateShader(const SkMatrix&) = 0;
+  virtual std::unique_ptr<PaintShader> CreateShader(const SkMatrix&) = 0;
   virtual bool IsLocalMatrixChanged(const SkMatrix&) const;
 
   RepeatMode repeat_mode_;
 
   Pattern(RepeatMode);
-  mutable sk_sp<PaintShader> cached_shader_;
+  mutable std::unique_ptr<PaintShader> cached_shader_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
index 9023571e..e215eb305 100644
--- a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.cpp
@@ -5,22 +5,24 @@
 #include "platform/graphics/PlaceholderImage.h"
 
 #include "platform/geometry/FloatRect.h"
-#include "platform/graphics/Color.h"
+#include "platform/geometry/IntPoint.h"
+#include "platform/geometry/IntRect.h"
+#include "platform/graphics/BitmapImage.h"
+#include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/ImageObserver.h"
+#include "platform/graphics/paint/PaintCanvas.h"
+#include "platform/graphics/paint/PaintFlags.h"
 #include "platform/graphics/paint/PaintRecord.h"
 #include "platform/graphics/paint/PaintRecorder.h"
+#include "platform/wtf/StdLibExtras.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkSize.h"
 
 namespace blink {
 
-namespace {
-
-// Gray with 40% opacity.
-const RGBA32 kFillColor = 0x66808080;
-
-}  // namespace
+PlaceholderImage::PlaceholderImage(ImageObserver* observer, const IntSize& size)
+    : Image(observer), size_(size) {}
 
 PlaceholderImage::~PlaceholderImage() {}
 
@@ -30,6 +32,7 @@
 
   const FloatRect dest_rect(0.0f, 0.0f, static_cast<float>(size_.Width()),
                             static_cast<float>(size_.Height()));
+
   PaintRecorder paint_recorder;
   Draw(paint_recorder.beginRecording(dest_rect), PaintFlags(), dest_rect,
        dest_rect, kDoNotRespectImageOrientation, kClampImageToSourceRect);
@@ -46,18 +49,68 @@
                             const PaintFlags& base_flags,
                             const FloatRect& dest_rect,
                             const FloatRect& src_rect,
-                            RespectImageOrientationEnum,
-                            ImageClampingMode) {
+                            RespectImageOrientationEnum respect_orientation,
+                            ImageClampingMode image_clamping_mode) {
   if (!src_rect.Intersects(FloatRect(0.0f, 0.0f,
                                      static_cast<float>(size_.Width()),
                                      static_cast<float>(size_.Height())))) {
     return;
   }
 
+  // Placeholder image visual specifications:
+  // https://docs.google.com/document/d/1BHeA1azbgCdZgCnr16VN2g7A9MHPQ_dwKn5szh8evMQ/edit
+
   PaintFlags flags(base_flags);
   flags.setStyle(PaintFlags::kFill_Style);
-  flags.setColor(kFillColor);
+  flags.setColor(SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9));
   canvas->drawRect(dest_rect, flags);
+
+  constexpr int kIconWidth = 24;
+  constexpr int kIconHeight = 24;
+  constexpr int kIconPaddingX = 8;
+  constexpr int kIconPaddingY = 5;
+
+  if (dest_rect.Width() < kIconWidth + 2 * kIconPaddingX ||
+      dest_rect.Height() < kIconHeight + 2 * kIconPaddingY) {
+    return;
+  }
+
+  DEFINE_STATIC_REF(Image, icon_image,
+                    (Image::LoadPlatformResource("placeholderIcon")));
+  DCHECK(!icon_image->IsNull());
+
+  FloatRect icon_dest_rect(
+      dest_rect.X() + (dest_rect.Width() - kIconWidth) / 2.0f,
+      dest_rect.Y() + (dest_rect.Height() - kIconHeight) / 2.0f, kIconWidth,
+      kIconHeight);
+
+  // Note that the |icon_image| is not scaled according to dest_rect / src_rect,
+  // and is always drawn at the same size. This is so that placeholder icons are
+  // visible (e.g. when replacing a large image that's scaled down to a small
+  // area) and so that all placeholder images on the same page look consistent.
+  canvas->drawImageRect(icon_image->PaintImageForCurrentFrame(),
+                        IntRect(IntPoint::Zero(), icon_image->Size()),
+                        icon_dest_rect, &base_flags,
+                        PaintCanvas::kFast_SrcRectConstraint);
+}
+
+void PlaceholderImage::DrawPattern(GraphicsContext& context,
+                                   const FloatRect& src_rect,
+                                   const FloatSize& scale,
+                                   const FloatPoint& phase,
+                                   SkBlendMode mode,
+                                   const FloatRect& dest_rect,
+                                   const FloatSize& repeat_spacing) {
+  DCHECK(context.Canvas());
+
+  PaintFlags flags = context.FillFlags();
+  flags.setBlendMode(mode);
+
+  // Ignore the pattern specifications and just draw a single placeholder image
+  // over the whole |dest_rect|. This is done in order to prevent repeated icons
+  // from cluttering tiled background images.
+  Draw(context.Canvas(), flags, dest_rect, src_rect,
+       kDoNotRespectImageOrientation, kClampImageToSourceRect);
 }
 
 void PlaceholderImage::DestroyDecodedData() {
diff --git a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
index f87d166..cb465863 100644
--- a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
+++ b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
@@ -15,7 +15,10 @@
 
 namespace blink {
 
+class FloatPoint;
 class FloatRect;
+class FloatSize;
+class GraphicsContext;
 class ImageObserver;
 
 // A generated placeholder image that shows a translucent gray rectangle.
@@ -42,8 +45,7 @@
   void DestroyDecodedData() override;
 
  private:
-  PlaceholderImage(ImageObserver* observer, const IntSize& size)
-      : Image(observer), size_(size) {}
+  PlaceholderImage(ImageObserver*, const IntSize&);
 
   bool CurrentFrameHasSingleSecurityOrigin() const override { return true; }
 
@@ -53,7 +55,16 @@
     return false;
   }
 
-  IntSize size_;
+  void DrawPattern(GraphicsContext&,
+                   const FloatRect& src_rect,
+                   const FloatSize& scale,
+                   const FloatPoint& phase,
+                   SkBlendMode,
+                   const FloatRect& dest_rect,
+                   const FloatSize& repeat_spacing = FloatSize()) override;
+
+  const IntSize size_;
+
   // Lazily initialized.
   sk_sp<SkImage> image_for_current_frame_;
 };
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintShader.h b/third_party/WebKit/Source/platform/graphics/paint/PaintShader.h
index 8bff588..9d1b8a8 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintShader.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintShader.h
@@ -9,9 +9,6 @@
 
 namespace blink {
 using cc::PaintShader;
-using cc::MakePaintShaderImage;
-using cc::MakePaintShaderRecord;
-using cc::WrapSkShader;
 }
 
 #endif  // PaintShader_h
diff --git a/third_party/WebKit/public/blink_image_resources.grd b/third_party/WebKit/public/blink_image_resources.grd
index 1361772..bc40341 100644
--- a/third_party/WebKit/public/blink_image_resources.grd
+++ b/third_party/WebKit/public/blink_image_resources.grd
@@ -36,6 +36,7 @@
       <structure type="chrome_scaled_image" name="IDR_SEARCH_CANCEL_PRESSED" file="blink/search_cancel_pressed.png" />
       <structure type="chrome_scaled_image" name="IDR_PASSWORD_GENERATION_ICON" file="blink/password_generation.png" />
       <structure type="chrome_scaled_image" name="IDR_PASSWORD_GENERATION_ICON_HOVER" file="blink/password_generation_hover.png" />
+      <structure type="chrome_scaled_image" name="IDR_PLACEHOLDER_ICON" file="blink/placeholder_icon.png" />
     </structures>
   </release>
 </grit>
diff --git a/third_party/WebKit/public/default_100_percent/blink/placeholder_icon.png b/third_party/WebKit/public/default_100_percent/blink/placeholder_icon.png
new file mode 100644
index 0000000..faa63ff
--- /dev/null
+++ b/third_party/WebKit/public/default_100_percent/blink/placeholder_icon.png
Binary files differ
diff --git a/third_party/closure_compiler/compile2.py b/third_party/closure_compiler/compile2.py
index a292c8d..224856d7 100755
--- a/third_party/closure_compiler/compile2.py
+++ b/third_party/closure_compiler/compile2.py
@@ -14,7 +14,6 @@
 import tempfile
 
 import processor
-import error_filter
 
 
 _CURRENT_DIR = os.path.join(os.path.dirname(__file__))
@@ -45,7 +44,6 @@
     self._target = None
     self._temp_files = []
     self._verbose = verbose
-    self._error_filter = error_filter.PromiseErrorFilter()
 
   def _nuke_temp_files(self):
     """Deletes any temp files this class knows about."""
@@ -122,29 +120,6 @@
     real_file = self._processor.get_file_from_line(match.group(1))
     return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number)
 
-  def _filter_errors(self, errors):
-    """Removes some extraneous errors. For example, we ignore:
-
-      Variable x first declared in /tmp/expanded/file
-
-    Because it's just a duplicated error (it'll only ever show up 2+ times).
-    We also ignore Promise-based errors:
-
-      found   : function (VolumeInfo): (Promise<(DirectoryEntry|null)>|null)
-      required: (function (Promise<VolumeInfo>): ?|null|undefined)
-
-    as templates don't work with Promises in all cases yet. See
-    https://github.com/google/closure-compiler/issues/715 for details.
-
-    Args:
-      errors: A list of string errors extracted from Closure Compiler output.
-
-    Return:
-      A slimmer, sleeker list of relevant errors (strings).
-    """
-    first_declared_in = lambda e: " first declared in " not in e
-    return self._error_filter.filter(filter(first_declared_in, errors))
-
   def _clean_up_error(self, error):
     """Reverse the effects that funky <include> preprocessing steps have on
     errors messages.
@@ -295,8 +270,7 @@
         f.write('')
 
     if process_includes:
-      filtered_errors = self._filter_errors(errors)
-      errors = map(self._clean_up_error, filtered_errors)
+      errors = map(self._clean_up_error, errors)
       output = self._format_errors(errors)
 
       if errors:
diff --git a/third_party/closure_compiler/error_filter.py b/third_party/closure_compiler/error_filter.py
deleted file mode 100644
index a5a549a..0000000
--- a/third_party/closure_compiler/error_filter.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Implement filtering out closure compiler errors due to incorrect type-
-checking on promise-based return types.
-
-The compiler's type-checker doesn't correctly unwrap Promised return values
-prior to type-checking them.  There are a couple of scenarios where this occurs,
-examples can be found below in the code that deals with each specific scenario.
-
-This filtering code applies a set of matchers to the errors that the compiler
-emits.  Each matcher fits a known pattern for compiler errors arising from the
-issue described above.  If any of the matchers matches an error, that error is
-filtered out of the error list.
-
-Note that this is just a coarse filter.  It doesn't, for example, check that the
-unwrapped promise type actually matches the type accepted by the next callback
-in the Promise chain.  Doing so would be the correct way to fix this problem,
-but that fix belongs in the compiler.
-
-"""
-
-import re
-
-
-class PromiseErrorFilter:
-  """Runs checks to filter out promise chain errors."""
-  def __init__(self):
-    self._allowed_error_patterns = [
-      ChainedPromisePattern(),
-      ReturnedPromisePattern()
-    ]
-
-  def filter(self, error_list):
-    """Filters out errors matching any of the allowed patterns.
-
-    Args:
-        error_list: A list of errors from the closure compiler.
-
-    Return:
-        A list of errors, with spurious Promise type errors removed.
-    """
-    return [error for error in error_list if not self._should_ignore(error)];
-
-  def _should_ignore(self, error):
-    """Check the given error against all the filters.  An error should be
-    ignored if it is a match for any of the allowed message patterns.
-
-    Args:
-        error: A single entry from the closure compiler error list.
-
-    Return:
-        True if the error should be ignored, False otherwise.
-    """
-    return any([pattern.match(error)
-                for pattern in self._allowed_error_patterns]);
-
-
-class ErrorPattern:
-  """A matcher for compiler error messages.  This matches compiler type errors,
-  which look like:
-    # ERROR - <some error message>
-    # found   : <some type expression>
-    # required: <some type expression>
-  The message and type expressions are customizable.
-  """
-  def __init__(self, msg, found_pattern, required_pattern):
-    # A string literal that is compared to the first line of the error.
-    self._error_msg = msg
-    # A regex for matching the found type.
-    self._found_line_regex = re.compile("found\s*:\s*" + found_pattern)
-    # A regex for matching the required type.
-    self._required_line_regex = re.compile("required:\s*" + required_pattern)
-
-  def match(self, error):
-    error_lines = error.split('\n')
-
-    # Match the error message to see if this pattern applies to the given error.
-    # If the error message matches, then compare the found and required lines.
-    if self._error_msg not in error_lines[0]:
-      return False
-    else:
-      return (self._found_line_regex.match(error_lines[1]) and
-              self._required_line_regex.match(error_lines[2]))
-
-
-class ChainedPromisePattern(ErrorPattern):
-  """Matcher for spurious errors arising from chained promises.  Example code:
-
-  Promise.resolve()
-    .then(
-        /** @return {!Promise<string>} */
-        function() { return Promise.resolve('foo'); })
-    .then(
-        /** @param {string} s */
-        function(s) { console.log(s); });
-
-  The compiler will emit an error that looks like
-
-  ERROR - actual parameter 1 of Promise.prototype.then does not match formal
-  parameter
-  found   : function (string): undefined
-  required: (function (Promise<string>): ?|null|undefined)
-  """
-  def __init__(self):
-    # Matches the initial error message.
-    msg = ("ERROR - actual parameter 1 of Promise.prototype.then "
-                  "does not match formal parameter")
-
-    # Examples:
-    # - function (string): Promise<string>
-    # - function ((SomeType|null)): SomeOtherType
-    found_pattern = "function\s*\(.*\):\s*.*"
-
-    # Examples:
-    # - (function(Promise<string>): ?|null|undefined)
-    required_pattern = "\(function\s*\(Promise<.*>\):\s*.*\)"
-
-    ErrorPattern.__init__(self, msg, found_pattern, required_pattern)
-
-
-class ReturnedPromisePattern(ErrorPattern):
-  """Matcher for spurious errors arising from Promised return values.  Example
-  code:
-
-  /** @return {!Promise<string>} */
-  var getStringAsync = function() {
-    /** @return {!Promise<string>} */
-    var generateString = function() {return Promise.resolve('foo');};
-    return Promise.resolve().then(generateString);
-  };
-
-  The compiler will emit an error that looks like
-
-  ERROR - inconsistent return type
-  found   : Promise<Promise<string>>
-  required: Promise<string>
-  """
-  def __init__(self):
-    # Matches the initial error message.
-    msg = "ERROR - inconsistent return type"
-
-    # Example:
-    # - Promise<Promise<string>>
-    found_pattern = "Promise<Promise<[^<>]*>"
-
-    # Example:
-    # - Promise<string>
-    required_pattern = "Promise<[^<>]*>"
-
-    ErrorPattern.__init__(self, msg, found_pattern, required_pattern)
diff --git a/third_party/flatbuffers/BUILD.gn b/third_party/flatbuffers/BUILD.gn
index 6407c35..a1554f2 100644
--- a/third_party/flatbuffers/BUILD.gn
+++ b/third_party/flatbuffers/BUILD.gn
@@ -78,9 +78,8 @@
 flatbuffer("flatbuffers_samplebuffer") {
   testonly = true
   sources = [
-    # Disabled as workaround for crbug.com/611351
-    # "src/tests/include_test/include_test1.fbs",
-    # "src/tests/include_test/sub/include_test2.fbs",
+    "src/tests/include_test/include_test1.fbs",
+    "src/tests/include_test/sub/include_test2.fbs",
     "src/tests/monster_test.fbs",
     "src/tests/namespace_test/namespace_test1.fbs",
     "src/tests/namespace_test/namespace_test2.fbs",
diff --git a/tools/binary_size/libsupersize/describe.py b/tools/binary_size/libsupersize/describe.py
index 19dd064..ad0412950 100644
--- a/tools/binary_size/libsupersize/describe.py
+++ b/tools/binary_size/libsupersize/describe.py
@@ -55,17 +55,31 @@
     self.recursive = recursive
 
   def _DescribeSectionSizes(self, section_sizes):
-    relevant_names = models.SECTION_TO_SECTION_NAME.values()
-    section_names = sorted(k for k in section_sizes.iterkeys()
-                           if k in relevant_names or k.startswith('.data'))
+    def include_in_totals(name):
+      return name != '.bss' and '(' not in name
+
     total_bytes = sum(v for k, v in section_sizes.iteritems()
-                      if k in section_names and k != '.bss')
+                      if include_in_totals(k))
+    max_bytes = max(abs(v) for k, v in section_sizes.iteritems()
+                    if include_in_totals(k))
+
+    def is_relevant_section(name, size):
+      # Show all sections containing symbols, plus relocations.
+      # As a catch-all, also include any section that comprises > 4% of the
+      # largest section. Use largest section rather than total so that it still
+      # works out when showing a diff containing +100, -100 (total=0).
+      return (name in models.SECTION_TO_SECTION_NAME.values() or
+              name in ('.rela.dyn', '.rel.dyn') or
+              include_in_totals(name) and abs(_Divide(size, max_bytes)) > .04)
+
+    section_names = sorted(k for k, v  in section_sizes.iteritems()
+                           if is_relevant_section(k, v))
     yield ''
     yield 'Section Sizes (Total={} ({} bytes)):'.format(
         _PrettySize(total_bytes), total_bytes)
     for name in section_names:
       size = section_sizes[name]
-      if name == '.bss':
+      if not include_in_totals(name):
         yield '    {}: {} ({} bytes) (not included in totals)'.format(
             name, _PrettySize(size), size)
       else:
@@ -79,8 +93,12 @@
       section_names = sorted(k for k in section_sizes.iterkeys()
                              if k not in section_names)
       for name in section_names:
-        yield '    {}: {} ({} bytes)'.format(
-            name, _PrettySize(section_sizes[name]), section_sizes[name])
+        not_included_part = ''
+        if not include_in_totals(name):
+          not_included_part = ' (not included in totals)'
+        yield '    {}: {} ({} bytes){}'.format(
+            name, _PrettySize(section_sizes[name]), section_sizes[name],
+            not_included_part)
 
   def _DescribeSymbol(self, sym, single_line=False):
     if sym.IsGroup():
diff --git a/tools/binary_size/libsupersize/testdata/Console.golden b/tools/binary_size/libsupersize/testdata/Console.golden
index 4208cc1..cb12723 100644
--- a/tools/binary_size/libsupersize/testdata/Console.golden
+++ b/tools/binary_size/libsupersize/testdata/Console.golden
@@ -56,13 +56,15 @@
     map_file_name=../test.map
     tool_prefix=tools/binary_size/libsupersize/testdata/mock_toolchain/
 
-Section Sizes (Total=41.8mb (43785380 bytes)):
+Section Sizes (Total=95.6mb (100233874 bytes)):
+    .ARM.exidx: 1.47mb (1536456 bytes) (1.5%)
     .bss: 1.24mb (1300456 bytes) (not included in totals)
-    .data: 99.4kb (101768 bytes) (0.2%)
-    .data.rel.ro: 1.02mb (1065224 bytes) (2.4%)
-    .data.rel.ro.local: 771kb (790024 bytes) (1.8%)
-    .rodata: 5.65mb (5927652 bytes) (13.5%)
-    .text: 34.2mb (35900712 bytes) (82.0%)
+    .data: 99.4kb (101768 bytes) (0.1%)
+    .rel.dyn: 2.53mb (2655384 bytes) (2.6%)
+    .rodata: 5.65mb (5927652 bytes) (5.9%)
+    .strtab: 33.2mb (34841854 bytes) (34.8%)
+    .symtab: 16.4mb (17166112 bytes) (17.1%)
+    .text: 34.2mb (35900712 bytes) (35.8%)
 
 Showing 45 symbols (42 unique) with total pss: 43785380 bytes
 .text=34.2mb     .rodata=5.65mb     .data*=1.87mb     .bss=512kb      total=41.8mb
diff --git a/tools/binary_size/libsupersize/testdata/Diff_Basic.golden b/tools/binary_size/libsupersize/testdata/Diff_Basic.golden
index b6b6070..8d756e1 100644
--- a/tools/binary_size/libsupersize/testdata/Diff_Basic.golden
+++ b/tools/binary_size/libsupersize/testdata/Diff_Basic.golden
@@ -9,8 +9,7 @@
 Section Sizes (Total=0 bytes (0 bytes)):
     .bss: 0 bytes (0 bytes) (not included in totals)
     .data: 0 bytes (0 bytes) (0.0%)
-    .data.rel.ro: 0 bytes (0 bytes) (0.0%)
-    .data.rel.ro.local: 0 bytes (0 bytes) (0.0%)
+    .rel.dyn: 0 bytes (0 bytes) (0.0%)
     .rodata: 0 bytes (0 bytes) (0.0%)
     .text: 0 bytes (0 bytes) (0.0%)
 
@@ -18,6 +17,8 @@
     .ARM.attributes: 0 bytes (0 bytes)
     .ARM.exidx: 0 bytes (0 bytes)
     .ARM.extab: 0 bytes (0 bytes)
+    .data.rel.ro: 0 bytes (0 bytes)
+    .data.rel.ro.local: 0 bytes (0 bytes)
     .dynamic: 0 bytes (0 bytes)
     .dynstr: 0 bytes (0 bytes)
     .dynsym: 0 bytes (0 bytes)
@@ -32,7 +33,6 @@
     .note.gnu.build-id: 0 bytes (0 bytes)
     .note.gnu.gold-version: 0 bytes (0 bytes)
     .plt: 0 bytes (0 bytes)
-    .rel.dyn: 0 bytes (0 bytes)
     .rel.plt: 0 bytes (0 bytes)
     .shstrtab: 0 bytes (0 bytes)
     .strtab: 0 bytes (0 bytes)
diff --git a/tools/binary_size/libsupersize/testdata/Diff_NullDiff.golden b/tools/binary_size/libsupersize/testdata/Diff_NullDiff.golden
index a780240..f1c0bd3 100644
--- a/tools/binary_size/libsupersize/testdata/Diff_NullDiff.golden
+++ b/tools/binary_size/libsupersize/testdata/Diff_NullDiff.golden
@@ -13,8 +13,7 @@
 Section Sizes (Total=0 bytes (0 bytes)):
     .bss: 0 bytes (0 bytes) (not included in totals)
     .data: 0 bytes (0 bytes) (0.0%)
-    .data.rel.ro: 0 bytes (0 bytes) (0.0%)
-    .data.rel.ro.local: 0 bytes (0 bytes) (0.0%)
+    .rel.dyn: 0 bytes (0 bytes) (0.0%)
     .rodata: 0 bytes (0 bytes) (0.0%)
     .text: 0 bytes (0 bytes) (0.0%)
 
diff --git a/tools/binary_size/libsupersize/testdata/FullDescription.golden b/tools/binary_size/libsupersize/testdata/FullDescription.golden
index de96cdd..5b94619 100644
--- a/tools/binary_size/libsupersize/testdata/FullDescription.golden
+++ b/tools/binary_size/libsupersize/testdata/FullDescription.golden
@@ -8,18 +8,21 @@
     map_file_name=../test.map
     tool_prefix=tools/binary_size/libsupersize/testdata/mock_toolchain/
 
-Section Sizes (Total=41.8mb (43785380 bytes)):
+Section Sizes (Total=95.6mb (100233874 bytes)):
+    .ARM.exidx: 1.47mb (1536456 bytes) (1.5%)
     .bss: 1.24mb (1300456 bytes) (not included in totals)
-    .data: 99.4kb (101768 bytes) (0.2%)
-    .data.rel.ro: 1.02mb (1065224 bytes) (2.4%)
-    .data.rel.ro.local: 771kb (790024 bytes) (1.8%)
-    .rodata: 5.65mb (5927652 bytes) (13.5%)
-    .text: 34.2mb (35900712 bytes) (82.0%)
+    .data: 99.4kb (101768 bytes) (0.1%)
+    .rel.dyn: 2.53mb (2655384 bytes) (2.6%)
+    .rodata: 5.65mb (5927652 bytes) (5.9%)
+    .strtab: 33.2mb (34841854 bytes) (34.8%)
+    .symtab: 16.4mb (17166112 bytes) (17.1%)
+    .text: 34.2mb (35900712 bytes) (35.8%)
 
 Other section sizes:
     .ARM.attributes: 60 bytes (60 bytes)
-    .ARM.exidx: 1.47mb (1536456 bytes)
     .ARM.extab: 179kb (183632 bytes)
+    .data.rel.ro: 1.02mb (1065224 bytes)
+    .data.rel.ro.local: 771kb (790024 bytes)
     .dynamic: 304 bytes (304 bytes)
     .dynstr: 3.93kb (4025 bytes)
     .dynsym: 6.34kb (6496 bytes)
@@ -34,11 +37,8 @@
     .note.gnu.build-id: 36 bytes (36 bytes)
     .note.gnu.gold-version: 28 bytes (28 bytes)
     .plt: 4.14kb (4244 bytes)
-    .rel.dyn: 2.53mb (2655384 bytes)
     .rel.plt: 2.75kb (2816 bytes)
     .shstrtab: 436 bytes (436 bytes)
-    .strtab: 33.2mb (34841854 bytes)
-    .symtab: 16.4mb (17166112 bytes)
 
 Section r: has 100.0% of 5927652 bytes accounted for from 10 symbols. 0 bytes are unaccounted for.
 * Padding accounts for 11 bytes (0.0%)
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py
index 04819c5..945ccbf 100644
--- a/tools/chrome_proxy/webdriver/common.py
+++ b/tools/chrome_proxy/webdriver/common.py
@@ -228,8 +228,9 @@
         'networkConnectionEnabled': True,
         'mobileEmulationEnabled': True,
       })
-      chrome_options.add_experimental_option('mobileEmulation',
-        {'deviceName': 'Google Nexus 5'})
+      if not self._flags.android:
+        chrome_options.add_experimental_option('mobileEmulation',
+          {'deviceName': 'Google Nexus 5'})
     for arg in self._chrome_args:
       chrome_options.add_argument(arg)
     self._logger.info('Starting Chrome with these flags: %s',
diff --git a/tools/chrome_proxy/webdriver/video.py b/tools/chrome_proxy/webdriver/video.py
index 7b203bef1..01e2866c 100644
--- a/tools/chrome_proxy/webdriver/video.py
+++ b/tools/chrome_proxy/webdriver/video.py
@@ -8,7 +8,6 @@
 from common import TestDriver
 from common import IntegrationTest
 from common import ParseFlags
-from decorators import NotAndroid
 from decorators import Slow
 
 from selenium.webdriver.common.by import By
@@ -86,18 +85,10 @@
 
   # Check that the compressed video can be seeked. Use a slow network to ensure
   # the entire video isn't downloaded before we have a chance to seek.
-  #
-  # This test cannot run on android because of control_network_connection=True.
-  # That option is used to reduce flakes that might happen on fast networks,
-  # where the video is completely downloaded before a seeking request can be
-  # sent. The test can be manually simulated by the following steps: set network
-  # emulation in DevTools on Android (via device inspector), load a video, pause
-  # the video, then seek and verify the seek continues to play the video.
   @Slow
-  @NotAndroid
   def testVideoSeeking(self):
     with TestDriver(control_network_connection=True) as t:
-      t.SetNetworkConnection("3G")
+      t.SetNetworkConnection("2G")
       t.AddChromeArg('--enable-spdy-proxy-auth')
       t.LoadURL(
           'http://check.googlezip.net/cacheable/video/'+
@@ -124,7 +115,10 @@
         };
         v.play();
         ''')
-      t.WaitForJavascriptExpression('window.testDone', 10)
+      if ParseFlags().android:
+        # v.play() won't work on Android, so give it a click instead.
+        t.FindElement(By.TAG_NAME, "video").click()
+      t.WaitForJavascriptExpression('window.testDone', 15)
       # Check request was proxied and we got a compressed video back.
       # We expect to make multiple requests for the video: ensure they
       # all have the same ETag.
diff --git a/tools/gn/function_get_label_info.cc b/tools/gn/function_get_label_info.cc
index 267c7b0..a67a3719 100644
--- a/tools/gn/function_get_label_info.cc
+++ b/tools/gn/function_get_label_info.cc
@@ -71,7 +71,7 @@
   get_label_info(":foo", "name")
   # Returns string "foo".
 
-  get_label_info("//foo/bar:baz", "gen_dir")
+  get_label_info("//foo/bar:baz", "target_gen_dir")
   # Returns string "//out/Debug/gen/foo/bar".
 )*";
 
diff --git a/tools/licenses.py b/tools/licenses.py
index bf0b53bc..ec63ff4 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -555,6 +555,19 @@
             template = template.replace('{{%s}}' % key, val)
         return template
 
+    def MetadataToTemplateEntry(metadata, entry_template, entry_id):
+        env = {
+            'name': metadata['Name'],
+            'url': metadata['URL'],
+            'license': open(metadata['License File'], 'rb').read(),
+            'id': str(entry_id),
+        }
+        return {
+            'name': metadata['Name'],
+            'content': EvaluateTemplate(entry_template, env),
+            'license_file': metadata['License File'],
+        }
+
     if gn_target:
         third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target)
 
@@ -575,7 +588,17 @@
                                            'about_credits_entry.tmpl')
 
     entry_template = open(entry_template_file).read()
+    entry_id = 0
     entries = []
+    # Start from Chromium's LICENSE file
+    chromium_license_metadata = {
+        'Name': 'The Chromium Project',
+        'URL': 'http://www.chromium.org',
+        'License File': os.path.join(_REPOSITORY_ROOT, 'LICENSE') }
+    entries.append(MetadataToTemplateEntry(chromium_license_metadata,
+        entry_template, entry_id))
+    entry_id += 1
+
     for path in third_party_dirs:
         try:
             metadata = ParseDir(path, _REPOSITORY_ROOT)
@@ -592,22 +615,11 @@
             # updated to provide --gn-target to this script.
             if path in KNOWN_NON_IOS_LIBRARIES:
                 continue
-        env = {
-            'name': metadata['Name'],
-            'url': metadata['URL'],
-            'license': open(metadata['License File'], 'rb').read(),
-        }
-        entry = {
-            'name': metadata['Name'],
-            'content': EvaluateTemplate(entry_template, env),
-            'license_file': metadata['License File'],
-        }
-        entries.append(entry)
-    # Sort by size in order to improve gzip compression ratio (puts similar
-    # licenses near each other). The licenses are re-sorted by the JavaScript
-    # when loaded.
-    entries.sort(key=lambda entry: (len(entry['content']),
-                                    entry['name'], entry['content']))
+        entries.append(MetadataToTemplateEntry(metadata, entry_template,
+            entry_id))
+        entry_id += 1
+
+    entries.sort(key=lambda entry: (entry['name'], entry['content']))
 
     entries_contents = '\n'.join([entry['content'] for entry in entries])
     file_template = open(file_template_file).read()
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3961fe27..3d4e3da 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -23485,6 +23485,13 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.ProgramCache.MemoryReleasedOnPressure" units="KB">
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Amount of memory released from the program cache on memory pressure.
+  </summary>
+</histogram>
+
 <histogram name="GPU.ProgramCache.MemorySizeAfterKb" units="KB">
   <owner>vmiura@chromium.org</owner>
   <summary>
@@ -77406,6 +77413,24 @@
   </summary>
 </histogram>
 
+<histogram name="Toolbar.Menu.NewIncognitoTabPresentationDuration" units="ms">
+  <owner>peterlaurens@chromium.org</owner>
+  <summary>
+    The number of millseconds between the user requesting a new incognito tab,
+    e.g. by tapping the New Incognito Tab entry in the main tools menu, and it
+    completing its animation on screen.
+  </summary>
+</histogram>
+
+<histogram name="Toolbar.Menu.NewTabPresentationDuration" units="ms">
+  <owner>peterlaurens@chromium.org</owner>
+  <summary>
+    The number of millseconds between the user requesting a new tab, e.g. by
+    tapping the New Tab entry in the main tools menu, and it completing its
+    animation on screen.
+  </summary>
+</histogram>
+
 <histogram name="Toolbar.ShowToolsMenuResponsiveness" units="ms">
   <owner>peterlaurens@chromium.org</owner>
   <summary>
@@ -77416,6 +77441,25 @@
   </summary>
 </histogram>
 
+<histogram name="Toolbar.TabSwitcher.NewIncognitoTabPresentationDuration"
+    units="ms">
+  <owner>peterlaurens@chromium.org</owner>
+  <summary>
+    The number of millseconds between the user requesting a new incognito tab,
+    from within the tab switcher, e.g. by tapping the New Tab button from the
+    tab switcher UI, and it completing its animation on screen.
+  </summary>
+</histogram>
+
+<histogram name="Toolbar.TabSwitcher.NewTabPresentationDuration" units="ms">
+  <owner>peterlaurens@chromium.org</owner>
+  <summary>
+    The number of millseconds between the user requesting a new tab, from within
+    the tab switcher UI, e.g. by tapping the New Tab button from the tab
+    switcher UI, and it completing its animation on screen.
+  </summary>
+</histogram>
+
 <histogram name="TopSites.NumberOfApplyBlacklist">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The number of times TopSitesImpl::ApplyBlacklist is called.</summary>
@@ -81329,6 +81373,15 @@
   </summary>
 </histogram>
 
+<histogram name="WebApk.Install.GooglePlayErrorCode" units="code">
+  <owner>hanxi@chromium.org</owner>
+  <owner>pkotwicz@chromium.org</owner>
+  <owner>yfriedman@chromium.org</owner>
+  <summary>
+    Records the error code when installing a WebAPK from Google Play fails.
+  </summary>
+</histogram>
+
 <histogram name="WebApk.Install.GooglePlayInstallResult"
     enum="WebApkGooglePlayInstallResult">
   <owner>hanxi@chromium.org</owner>
diff --git a/ui/app_list/app_list_features.cc b/ui/app_list/app_list_features.cc
index b3c89fa..770e0e03 100644
--- a/ui/app_list/app_list_features.cc
+++ b/ui/app_list/app_list_features.cc
@@ -35,13 +35,13 @@
 
 int APP_LIST_EXPORT AnswerCardMaxWidth() {
   static const int max_width = base::GetFieldTrialParamByFeatureAsInt(
-      kEnableAnswerCard, "CardMaxWidth", 640);
+      kEnableAnswerCard, "CardMaxWidth", 608);
   return max_width;
 }
 
 int APP_LIST_EXPORT AnswerCardMaxHeight() {
   static const int max_height = base::GetFieldTrialParamByFeatureAsInt(
-      kEnableAnswerCard, "CardMaxHeight", 288);
+      kEnableAnswerCard, "CardMaxHeight", 266);
   return max_height;
 }
 
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc
index ee32bc1..0132a12 100644
--- a/ui/app_list/views/search_result_answer_card_view.cc
+++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -15,6 +15,11 @@
 
 namespace app_list {
 
+namespace {
+constexpr int kVerticalPadding = 11;
+constexpr int kHorizontalPadding = 16;
+}
+
 // Container of the search answer view.
 class SearchResultAnswerCardView::SearchAnswerContainerView
     : public views::CustomButton,
@@ -25,11 +30,11 @@
       : CustomButton(this), view_delegate_(view_delegate) {
     // Center the card horizontally in the container.
     views::BoxLayout* answer_container_layout =
-        new views::BoxLayout(views::BoxLayout::kHorizontal);
+        new views::BoxLayout(views::BoxLayout::kHorizontal,
+                             gfx::Insets(kVerticalPadding, kHorizontalPadding));
     answer_container_layout->set_main_axis_alignment(
-        views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
+        views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     SetLayoutManager(answer_container_layout);
-    SetVisible(false);
   }
 
   void SetSelected(bool selected) {
@@ -58,8 +63,6 @@
       search_result_->AddObserver(this);
       SetAccessibleName(search_result_->title());
     }
-
-    SetVisible(new_result_view != nullptr);
   }
 
   // views::CustomButton overrides:
@@ -138,6 +141,7 @@
 
   search_answer_container_view_->SetSearchResult(
       have_result ? display_results[0] : nullptr);
+  parent()->SetVisible(have_result);
 
   set_container_score(have_result ? display_results.front()->relevance() : 0);
   return have_result ? 1 : 0;
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index cda5251..67e6b72 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -27,8 +27,9 @@
 
 namespace {
 
-const int kGroupSpacing = 6;
-const int kTopPadding = 8;
+constexpr int kGroupSpacing = 6;
+constexpr int kTopPadding = 8;
+constexpr int kFullscreenHeight = 440;
 
 // The z-height of the search box and cards in this view.
 const int kSearchResultZHeight = 1;
@@ -46,12 +47,17 @@
     AddChildView(content_view);
   }
 
+  // views::View overrides:
+  const char* GetClassName() const override { return "SearchCardView"; }
+
   ~SearchCardView() override {}
 };
 
 }  // namespace
 
-SearchResultPageView::SearchResultPageView() : selected_index_(0) {
+SearchResultPageView::SearchResultPageView()
+    : selected_index_(0),
+      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
   gfx::ShadowValue shadow = GetShadowForZHeight(kSearchResultZHeight);
   std::unique_ptr<views::Border> border(new views::ShadowBorder(shadow));
 
@@ -213,10 +219,11 @@
 
 gfx::Rect SearchResultPageView::GetPageBoundsForState(
     AppListModel::State state) const {
-  gfx::Rect onscreen_bounds =
-      features::IsAnswerCardEnabled() && !features::IsAnswerCardDarkRunEnabled()
-          ? GetFullContentsBounds()
-          : GetDefaultContentsBounds();
+  gfx::Rect onscreen_bounds = GetDefaultContentsBounds();
+
+  if (is_fullscreen_app_list_enabled_)
+    onscreen_bounds.set_height(kFullscreenHeight);
+
   switch (state) {
     case AppListModel::STATE_SEARCH_RESULTS:
       return onscreen_bounds;
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index 9dcd5489..f929ed63 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -66,6 +66,9 @@
   // -1 indicates no selection.
   int selected_index_;
 
+  // Whether launcher is shown in fullscreen mode.
+  bool const is_fullscreen_app_list_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(SearchResultPageView);
 };
 
diff --git a/ui/compositor/debug_utils.cc b/ui/compositor/debug_utils.cc
index 3d4837e3..75f3457 100644
--- a/ui/compositor/debug_utils.cc
+++ b/ui/compositor/debug_utils.cc
@@ -92,7 +92,7 @@
 
     *out << '\n' << property_indent_str;
     *out << "rotation: ";
-    *out << std::acos(decomp.quaternion[3]) * 360.0 / M_PI;
+    *out << std::acos(decomp.quaternion.w()) * 360.0 / M_PI;
 
     *out << '\n' << property_indent_str;
     *out << "scale: " << decomp.scale[0];
diff --git a/ui/compositor/transform_animation_curve_adapter.cc b/ui/compositor/transform_animation_curve_adapter.cc
index a41df05..e698be6 100644
--- a/ui/compositor/transform_animation_curve_adapter.cc
+++ b/ui/compositor/transform_animation_curve_adapter.cc
@@ -46,12 +46,9 @@
     return initial_value_;
   double progress = cc::TimeUtil::Divide(t, duration_);
 
-  gfx::DecomposedTransform to_return;
-  gfx::BlendDecomposedTransforms(&to_return,
-                                 decomposed_target_value_,
-                                 decomposed_initial_value_,
-                                 gfx::Tween::CalculateValue(tween_type_,
-                                                            progress));
+  gfx::DecomposedTransform to_return = gfx::BlendDecomposedTransforms(
+      decomposed_target_value_, decomposed_initial_value_,
+      gfx::Tween::CalculateValue(tween_type_, progress));
   return gfx::ComposeTransform(to_return);
 }
 
diff --git a/ui/events/ozone/evdev/input_controller_evdev.cc b/ui/events/ozone/evdev/input_controller_evdev.cc
index 1de725c..aac3e2a0 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.cc
+++ b/ui/events/ozone/evdev/input_controller_evdev.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 
+#include "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/events/devices/device_data_manager.h"
@@ -85,9 +86,9 @@
   keyboard_->GetAutoRepeatRate(delay, interval);
 }
 
-bool InputControllerEvdev::SetCurrentLayoutByName(
+void InputControllerEvdev::SetCurrentLayoutByName(
     const std::string& layout_name) {
-  return keyboard_->SetCurrentLayoutByName(layout_name);
+  keyboard_->SetCurrentLayoutByName(layout_name);
 }
 
 void InputControllerEvdev::SetInternalTouchpadEnabled(bool enabled) {
@@ -159,20 +160,19 @@
 }
 
 void InputControllerEvdev::GetTouchDeviceStatus(
-    const GetTouchDeviceStatusReply& reply) {
+    GetTouchDeviceStatusReply reply) {
   if (input_device_factory_)
-    input_device_factory_->GetTouchDeviceStatus(reply);
+    input_device_factory_->GetTouchDeviceStatus(std::move(reply));
   else
-    reply.Run(base::WrapUnique(new std::string));
+    std::move(reply).Run(std::string());
 }
 
-void InputControllerEvdev::GetTouchEventLog(
-    const base::FilePath& out_dir,
-    const GetTouchEventLogReply& reply) {
+void InputControllerEvdev::GetTouchEventLog(const base::FilePath& out_dir,
+                                            GetTouchEventLogReply reply) {
   if (input_device_factory_)
-    input_device_factory_->GetTouchEventLog(out_dir, reply);
+    input_device_factory_->GetTouchEventLog(out_dir, std::move(reply));
   else
-    reply.Run(base::WrapUnique(new std::vector<base::FilePath>));
+    std::move(reply).Run(std::vector<base::FilePath>());
 }
 
 void InputControllerEvdev::ScheduleUpdateDeviceSettings() {
diff --git a/ui/events/ozone/evdev/input_controller_evdev.h b/ui/events/ozone/evdev/input_controller_evdev.h
index a590b358..6d1adcf 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.h
+++ b/ui/events/ozone/evdev/input_controller_evdev.h
@@ -48,7 +48,7 @@
                          const base::TimeDelta& interval) override;
   void GetAutoRepeatRate(base::TimeDelta* delay,
                          base::TimeDelta* interval) override;
-  bool SetCurrentLayoutByName(const std::string& layout_name) override;
+  void SetCurrentLayoutByName(const std::string& layout_name) override;
   void SetTouchEventLoggingEnabled(bool enabled) override;
   void SetTouchpadSensitivity(int value) override;
   void SetTapToClick(bool enabled) override;
@@ -58,9 +58,9 @@
   void SetMouseSensitivity(int value) override;
   void SetPrimaryButtonRight(bool right) override;
   void SetTapToClickPaused(bool state) override;
-  void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) override;
+  void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override;
   void GetTouchEventLog(const base::FilePath& out_dir,
-                        const GetTouchEventLogReply& reply) override;
+                        GetTouchEventLogReply reply) override;
   void SetInternalTouchpadEnabled(bool enabled) override;
   bool IsInternalTouchpadEnabled() const override;
   void SetTouchscreensEnabled(bool enabled) override;
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index 4eb65773..ab3ec28 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -267,24 +267,22 @@
 }
 
 void InputDeviceFactoryEvdev::GetTouchDeviceStatus(
-    const GetTouchDeviceStatusReply& reply) {
-  std::unique_ptr<std::string> status(new std::string);
+    InputController::GetTouchDeviceStatusReply reply) {
+  std::string status;
 #if defined(USE_EVDEV_GESTURES)
-  DumpTouchDeviceStatus(gesture_property_provider_.get(), status.get());
+  DumpTouchDeviceStatus(gesture_property_provider_.get(), &status);
 #endif
-  reply.Run(std::move(status));
+  std::move(reply).Run(status);
 }
 
 void InputDeviceFactoryEvdev::GetTouchEventLog(
     const base::FilePath& out_dir,
-    const GetTouchEventLogReply& reply) {
-  std::unique_ptr<std::vector<base::FilePath>> log_paths(
-      new std::vector<base::FilePath>);
+    InputController::GetTouchEventLogReply reply) {
 #if defined(USE_EVDEV_GESTURES)
   DumpTouchEventLog(converters_, gesture_property_provider_.get(), out_dir,
-                    std::move(log_paths), reply);
+                    std::move(reply));
 #else
-  reply.Run(std::move(log_paths));
+  std::move(reply).Run(std::vector<base::FilePath>());
 #endif
 }
 
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.h b/ui/events/ozone/evdev/input_device_factory_evdev.h
index ddfbc999..3c20987 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.h
@@ -20,6 +20,7 @@
 #include "ui/events/ozone/evdev/event_device_info.h"
 #include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
 #include "ui/events/ozone/evdev/input_device_settings_evdev.h"
+#include "ui/ozone/public/input_controller.h"
 
 namespace ui {
 
@@ -34,11 +35,6 @@
 class GesturePropertyProvider;
 #endif
 
-typedef base::Callback<void(std::unique_ptr<std::string>)>
-    GetTouchDeviceStatusReply;
-typedef base::Callback<void(std::unique_ptr<std::vector<base::FilePath>>)>
-    GetTouchEventLogReply;
-
 // Manager for event device objects. All device I/O starts here.
 class EVENTS_OZONE_EVDEV_EXPORT InputDeviceFactoryEvdev {
  public:
@@ -61,9 +57,9 @@
 
   // Bits from InputController that have to be answered on IO.
   void UpdateInputDeviceSettings(const InputDeviceSettingsEvdev& settings);
-  void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply);
+  void GetTouchDeviceStatus(InputController::GetTouchDeviceStatusReply reply);
   void GetTouchEventLog(const base::FilePath& out_dir,
-                        const GetTouchEventLogReply& reply);
+                        InputController::GetTouchEventLogReply reply);
 
   base::WeakPtr<InputDeviceFactoryEvdev> GetWeakPtr();
 
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
index 8eb3697..2696967 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
@@ -5,6 +5,7 @@
 #include "ui/events/ozone/evdev/input_device_factory_evdev_proxy.h"
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/events/ozone/evdev/input_device_factory_evdev.h"
 
@@ -14,19 +15,19 @@
 
 void ForwardGetTouchDeviceStatusReply(
     scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
-    const GetTouchDeviceStatusReply& reply,
-    std::unique_ptr<std::string> status) {
+    InputController::GetTouchDeviceStatusReply reply,
+    const std::string& status) {
   // Thread hop back to UI for reply.
-  reply_runner->PostTask(FROM_HERE, base::Bind(reply, base::Passed(&status)));
+  reply_runner->PostTask(FROM_HERE, base::BindOnce(std::move(reply), status));
 }
 
 void ForwardGetTouchEventLogReply(
     scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
-    const GetTouchEventLogReply& reply,
-    std::unique_ptr<std::vector<base::FilePath>> log_paths) {
+    InputController::GetTouchEventLogReply reply,
+    const std::vector<base::FilePath>& log_paths) {
   // Thread hop back to UI for reply.
   reply_runner->PostTask(FROM_HERE,
-                         base::Bind(reply, base::Passed(&log_paths)));
+                         base::BindOnce(std::move(reply), log_paths));
 }
 
 }  // namespace
@@ -74,24 +75,26 @@
 }
 
 void InputDeviceFactoryEvdevProxy::GetTouchDeviceStatus(
-    const GetTouchDeviceStatusReply& reply) {
+    InputController::GetTouchDeviceStatusReply reply) {
   task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&InputDeviceFactoryEvdev::GetTouchDeviceStatus,
-                 input_device_factory_,
-                 base::Bind(&ForwardGetTouchDeviceStatusReply,
-                            base::ThreadTaskRunnerHandle::Get(), reply)));
+      base::BindOnce(&InputDeviceFactoryEvdev::GetTouchDeviceStatus,
+                     input_device_factory_,
+                     base::BindOnce(&ForwardGetTouchDeviceStatusReply,
+                                    base::ThreadTaskRunnerHandle::Get(),
+                                    std::move(reply))));
 }
 
 void InputDeviceFactoryEvdevProxy::GetTouchEventLog(
     const base::FilePath& out_dir,
-    const GetTouchEventLogReply& reply) {
+    InputController::GetTouchEventLogReply reply) {
   task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&InputDeviceFactoryEvdev::GetTouchEventLog,
-                 input_device_factory_, out_dir,
-                 base::Bind(&ForwardGetTouchEventLogReply,
-                            base::ThreadTaskRunnerHandle::Get(), reply)));
+      base::BindOnce(&InputDeviceFactoryEvdev::GetTouchEventLog,
+                     input_device_factory_, out_dir,
+                     base::BindOnce(&ForwardGetTouchEventLogReply,
+                                    base::ThreadTaskRunnerHandle::Get(),
+                                    std::move(reply))));
 }
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
index 1b736ea..c7c07070 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
+#include "ui/ozone/public/input_controller.h"
 
 namespace ui {
 
@@ -23,11 +24,6 @@
 class InputDeviceFactoryEvdev;
 struct InputDeviceSettingsEvdev;
 
-typedef base::Callback<void(std::unique_ptr<std::string>)>
-    GetTouchDeviceStatusReply;
-typedef base::Callback<void(std::unique_ptr<std::vector<base::FilePath>>)>
-    GetTouchEventLogReply;
-
 // Thread safe proxy for InputDeviceFactoryEvdev.
 //
 // This is used on the UI thread to proxy calls to the real object on
@@ -47,9 +43,9 @@
   void SetCapsLockLed(bool enabled);
   void SetTouchEventLoggingEnabled(bool enabled);
   void UpdateInputDeviceSettings(const InputDeviceSettingsEvdev& settings);
-  void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply);
+  void GetTouchDeviceStatus(InputController::GetTouchDeviceStatusReply reply);
   void GetTouchEventLog(const base::FilePath& out_dir,
-                        const GetTouchEventLogReply& reply);
+                        InputController::GetTouchEventLogReply reply);
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc
index 0d6bc53..3b1f400 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.cc
@@ -173,8 +173,8 @@
         converters,
     GesturePropertyProvider* provider,
     const base::FilePath& out_dir,
-    std::unique_ptr<std::vector<base::FilePath>> log_paths,
-    const GetTouchEventLogReply& reply) {
+    InputController::GetTouchEventLogReply reply) {
+  std::vector<base::FilePath> log_paths;
   // Get device ids.
   std::vector<int> ids;
   provider->GetDeviceIdsByType(DT_ALL, &ids);
@@ -215,9 +215,9 @@
     // Historically, we compress touchpad/mouse logs with gzip before tarring
     // them up. We DONT compress touchscreen logs though.
     log_paths_to_be_compressed->push_back(gesture_log_filename);
-    log_paths->push_back(base::FilePath(gesture_log_filename));
+    log_paths.push_back(base::FilePath(gesture_log_filename));
     log_paths_to_be_compressed->push_back(evdev_log_filename);
-    log_paths->push_back(base::FilePath(evdev_log_filename));
+    log_paths.push_back(base::FilePath(evdev_log_filename));
   }
 
   for (const auto& converter_pair : converters) {
@@ -228,7 +228,7 @@
           out_dir, "evdev_input_events_", now, converter->id());
       base::Move(base::FilePath(kInputEventsLogFile),
                  base::FilePath(touch_evdev_log_filename));
-      log_paths->push_back(base::FilePath(touch_evdev_log_filename));
+      log_paths.push_back(base::FilePath(touch_evdev_log_filename));
     }
   }
 
@@ -238,7 +238,7 @@
       {base::MayBlock(), base::TaskPriority::BACKGROUND,
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       base::Bind(&CompressDumpedLog, base::Passed(&log_paths_to_be_compressed)),
-      base::Bind(reply, base::Passed(&log_paths)));
+      base::BindOnce(std::move(reply), log_paths));
 }
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h
index ab6ffcc..da2a356 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h
@@ -9,10 +9,10 @@
 #include <string>
 #include <vector>
 
-#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
+#include "ui/ozone/public/input_controller.h"
 
 namespace ui {
 
@@ -26,9 +26,6 @@
 
 class GesturePropertyProvider;
 
-typedef base::Callback<void(std::unique_ptr<std::vector<base::FilePath>>)>
-    GetTouchEventLogReply;
-
 // Utility functions for generating gesture related logs. These logs will be
 // included in user feedback reports.
 void DumpTouchDeviceStatus(GesturePropertyProvider* provider,
@@ -39,8 +36,7 @@
         converter,
     GesturePropertyProvider* provider,
     const base::FilePath& out_dir,
-    std::unique_ptr<std::vector<base::FilePath>> log_paths,
-    const GetTouchEventLogReply& reply);
+    InputController::GetTouchEventLogReply reply);
 
 }  // namespace ui
 
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index e78c050c..8b34733 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -658,6 +658,7 @@
       "geometry/point3_unittest.cc",
       "geometry/point_unittest.cc",
       "geometry/quad_unittest.cc",
+      "geometry/quaternion_unittest.cc",
       "geometry/rect_unittest.cc",
       "geometry/safe_integer_conversions_unittest.cc",
       "geometry/scroll_offset_unittest.cc",
diff --git a/ui/gfx/geometry/BUILD.gn b/ui/gfx/geometry/BUILD.gn
index 0bf2db1..7bbae4d 100644
--- a/ui/gfx/geometry/BUILD.gn
+++ b/ui/gfx/geometry/BUILD.gn
@@ -29,6 +29,8 @@
     "point_f.h",
     "quad_f.cc",
     "quad_f.h",
+    "quaternion.cc",
+    "quaternion.h",
     "rect.cc",
     "rect.h",
     "rect_conversions.cc",
diff --git a/ui/gfx/geometry/quaternion.cc b/ui/gfx/geometry/quaternion.cc
new file mode 100644
index 0000000..283df36
--- /dev/null
+++ b/ui/gfx/geometry/quaternion.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/quaternion.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+namespace {
+
+const double kEpsilon = 1e-5;
+
+}  // namespace
+
+Quaternion::Quaternion(const Vector3dF& axis, double theta) {
+  // Rotation angle is the product of |angle| and the magnitude of |axis|.
+  double length = axis.Length();
+  if (std::abs(length) < kEpsilon)
+    return;
+
+  Vector3dF normalized = axis;
+  normalized.Scale(1.0 / length);
+
+  theta *= 0.5;
+  double s = sin(theta);
+  x_ = normalized.x() * s;
+  y_ = normalized.y() * s;
+  z_ = normalized.z() * s;
+  w_ = cos(theta);
+}
+
+// Taken from http://www.w3.org/TR/css3-transforms/.
+Quaternion Quaternion::Slerp(const Quaternion& q, double t) const {
+  double dot = x_ * q.x_ + y_ * q.y_ + z_ * q.z_ + w_ * q.w_;
+
+  // Clamp dot to -1.0 <= dot <= 1.0.
+  dot = std::min(std::max(dot, -1.0), 1.0);
+
+  // Quaternions are facing the same direction.
+  if (std::abs(dot - 1.0) < kEpsilon || std::abs(dot + 1.0) < kEpsilon)
+    return *this;
+
+  // TODO(vmpstr): In case the dot is 0, the vectors are exactly opposite
+  // of each other. In this case, it's technically not correct to just pick one
+  // of the vectors, we instead need to pick how to interpolate. However, the
+  // spec isn't clear on this. If we don't handle the -1 case explicitly, it
+  // results in inf and nans however, which is worse. See crbug.com/506543 for
+  // more discussion.
+  if (std::abs(dot) < kEpsilon)
+    return *this;
+
+  double denom = std::sqrt(1.0 - dot * dot);
+  double theta = std::acos(dot);
+  double w = std::sin(t * theta) * (1.0 / denom);
+
+  double s1 = std::cos(t * theta) - dot * w;
+  double s2 = w;
+
+  return (s1 * *this) + (s2 * q);
+}
+
+Quaternion Quaternion::Lerp(const Quaternion& q, double t) const {
+  return ((1.0 - t) * *this) + (t * q);
+}
+
+std::string Quaternion::ToString() const {
+  return base::StringPrintf("[%f %f %f %f]", x_, y_, z_, w_);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quaternion.h b/ui/gfx/geometry/quaternion.h
new file mode 100644
index 0000000..9b214a0
--- /dev/null
+++ b/ui/gfx/geometry/quaternion.h
@@ -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.
+
+#ifndef UI_GFX_GEOMETRY_QUATERNION_
+#define UI_GFX_GEOMETRY_QUATERNION_
+
+#include <string>
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class Vector3dF;
+
+class GFX_EXPORT Quaternion {
+ public:
+  constexpr Quaternion() = default;
+  constexpr Quaternion(double x, double y, double z, double w)
+      : x_(x), y_(y), z_(z), w_(w) {}
+  Quaternion(const Vector3dF& axis, double angle);
+
+  constexpr double x() const { return x_; }
+  void set_x(double x) { x_ = x; }
+
+  constexpr double y() const { return y_; }
+  void set_y(double y) { y_ = y; }
+
+  constexpr double z() const { return z_; }
+  void set_z(double z) { z_ = z; }
+
+  constexpr double w() const { return w_; }
+  void set_w(double w) { w_ = w; }
+
+  Quaternion operator+(const Quaternion& q) const {
+    return {q.x_ + x_, q.y_ + y_, q.z_ + z_, q.w_ + w_};
+  }
+
+  Quaternion operator*(const Quaternion& q) const {
+    return {w_ * q.x_ + x_ * q.w_ + y_ * q.z_ - z_ * q.y_,
+            w_ * q.y_ - x_ * q.z_ + y_ * q.w_ + z_ * q.x_,
+            w_ * q.z_ + x_ * q.y_ - y_ * q.x_ + z_ * q.w_,
+            w_ * q.w_ - x_ * q.x_ - y_ * q.y_ - z_ * q.z_};
+  }
+
+  Quaternion inverse() const { return {-x_, -y_, -z_, w_}; }
+
+  // Blends with the given quaternion, |q|, via spherical linear interpolation.
+  // Values of |t| in the range [0, 1] will interpolate between |this| and |q|,
+  // and values outside that range will extrapolate beyond in either direction.
+  Quaternion Slerp(const Quaternion& q, double t) const;
+
+  // Blends with the given quaternion, |q|, via linear interpolation. This is
+  // rarely what you want. Use only if you know what you're doing.
+  // Values of |t| in the range [0, 1] will interpolate between |this| and |q|,
+  // and values outside that range will extrapolate beyond in either direction.
+  Quaternion Lerp(const Quaternion& q, double t) const;
+
+  std::string ToString() const;
+
+ private:
+  double x_ = 0.0;
+  double y_ = 0.0;
+  double z_ = 0.0;
+  double w_ = 1.0;
+};
+
+// |s| is an arbitrary, real constant.
+inline Quaternion operator*(const Quaternion& q, double s) {
+  return Quaternion(q.x() * s, q.y() * s, q.z() * s, q.w() * s);
+}
+
+// |s| is an arbitrary, real constant.
+inline Quaternion operator*(double s, const Quaternion& q) {
+  return Quaternion(q.x() * s, q.y() * s, q.z() * s, q.w() * s);
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_QUATERNION_
diff --git a/ui/gfx/geometry/quaternion_unittest.cc b/ui/gfx/geometry/quaternion_unittest.cc
new file mode 100644
index 0000000..b66d0ffd8
--- /dev/null
+++ b/ui/gfx/geometry/quaternion_unittest.cc
@@ -0,0 +1,158 @@
+// 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.
+
+#define _USE_MATH_DEFINES  // For VC++ to get M_PI. This has to be first.
+
+#include <cmath>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+namespace {
+
+const double kEpsilon = 1e-7;
+
+}  // namespace
+
+TEST(QuatTest, DefaultConstruction) {
+  Quaternion q;
+  EXPECT_FLOAT_EQ(0.0, q.x());
+  EXPECT_FLOAT_EQ(0.0, q.y());
+  EXPECT_FLOAT_EQ(0.0, q.z());
+  EXPECT_FLOAT_EQ(1.0, q.w());
+}
+
+TEST(QuatTest, AxisAngleCommon) {
+  float radians = 0.5;
+  Quaternion q(Vector3dF(1.0f, 0.0f, 0.0f), radians);
+  EXPECT_FLOAT_EQ(std::sin(radians / 2), q.x());
+  EXPECT_FLOAT_EQ(0.0, q.y());
+  EXPECT_FLOAT_EQ(0.0, q.z());
+  EXPECT_FLOAT_EQ(std::cos(radians / 2), q.w());
+}
+
+TEST(QuatTest, AxisAngleWithZeroLengthAxis) {
+  Quaternion q(Vector3dF(0.0f, 0.0f, 0.0f), 0.5);
+  // If the axis of zero length, we should assume the default values.
+  EXPECT_FLOAT_EQ(0.0, q.x());
+  EXPECT_FLOAT_EQ(0.0, q.y());
+  EXPECT_FLOAT_EQ(0.0, q.z());
+  EXPECT_FLOAT_EQ(1.0, q.w());
+}
+
+TEST(QuatTest, Addition) {
+  float values[] = {0.f, 1.0f, 100.0f};
+  for (size_t i = 0; i < arraysize(values); ++i) {
+    float t = values[i];
+    Quaternion a(t, 2 * t, 3 * t, 4 * t);
+    Quaternion b(5 * t, 4 * t, 3 * t, 2 * t);
+    Quaternion sum = a + b;
+    EXPECT_FLOAT_EQ(6 * t, sum.x());
+    EXPECT_FLOAT_EQ(6 * t, sum.y());
+    EXPECT_FLOAT_EQ(6 * t, sum.z());
+    EXPECT_FLOAT_EQ(6 * t, sum.w());
+  }
+}
+
+TEST(QuatTest, Multiplication) {
+  struct {
+    Quaternion a;
+    Quaternion b;
+    Quaternion expected;
+  } cases[] = {
+      {Quaternion(1, 0, 0, 0), Quaternion(1, 0, 0, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 1, 0, 0), Quaternion(0, 1, 0, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 0, 1, 0), Quaternion(0, 0, 1, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1)},
+      {Quaternion(1, 2, 3, 4), Quaternion(5, 6, 7, 8),
+       Quaternion(24, 48, 48, -6)},
+      {Quaternion(5, 6, 7, 8), Quaternion(1, 2, 3, 4),
+       Quaternion(32, 32, 56, -6)},
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    Quaternion product = cases[i].a * cases[i].b;
+    EXPECT_FLOAT_EQ(cases[i].expected.x(), product.x());
+    EXPECT_FLOAT_EQ(cases[i].expected.y(), product.y());
+    EXPECT_FLOAT_EQ(cases[i].expected.z(), product.z());
+    EXPECT_FLOAT_EQ(cases[i].expected.w(), product.w());
+  }
+}
+
+TEST(QuatTest, Scaling) {
+  float values[] = {0.f, 1.0f, 100.0f};
+  for (size_t i = 0; i < arraysize(values); ++i) {
+    Quaternion q(1, 2, 3, 4);
+    Quaternion qs = q * values[i];
+    Quaternion sq = values[i] * q;
+    EXPECT_FLOAT_EQ(1 * values[i], qs.x());
+    EXPECT_FLOAT_EQ(2 * values[i], qs.y());
+    EXPECT_FLOAT_EQ(3 * values[i], qs.z());
+    EXPECT_FLOAT_EQ(4 * values[i], qs.w());
+    EXPECT_FLOAT_EQ(1 * values[i], sq.x());
+    EXPECT_FLOAT_EQ(2 * values[i], sq.y());
+    EXPECT_FLOAT_EQ(3 * values[i], sq.z());
+    EXPECT_FLOAT_EQ(4 * values[i], sq.w());
+  }
+}
+
+TEST(QuatTest, Lerp) {
+  for (size_t i = 0; i < 100; ++i) {
+    Quaternion a(0, 0, 0, 0);
+    Quaternion b(1, 2, 3, 4);
+    float t = static_cast<float>(i) / 100.0f;
+    Quaternion interpolated = a.Lerp(b, t);
+    EXPECT_FLOAT_EQ(1 * t, interpolated.x());
+    EXPECT_FLOAT_EQ(2 * t, interpolated.y());
+    EXPECT_FLOAT_EQ(3 * t, interpolated.z());
+    EXPECT_FLOAT_EQ(4 * t, interpolated.w());
+  }
+}
+
+TEST(QuatTest, Slerp) {
+  Vector3dF axis(1, 1, 1);
+  double start_radians = -0.5;
+  double stop_radians = 0.5;
+  Quaternion start(axis, start_radians);
+  Quaternion stop(axis, stop_radians);
+
+  for (size_t i = 0; i < 100; ++i) {
+    float t = static_cast<float>(i) / 100.0f;
+    double radians = (1.0 - t) * start_radians + t * stop_radians;
+    Quaternion expected(axis, radians);
+    Quaternion interpolated = start.Slerp(stop, t);
+    EXPECT_NEAR(expected.x(), interpolated.x(), kEpsilon);
+    EXPECT_NEAR(expected.y(), interpolated.y(), kEpsilon);
+    EXPECT_NEAR(expected.z(), interpolated.z(), kEpsilon);
+    EXPECT_NEAR(expected.w(), interpolated.w(), kEpsilon);
+  }
+}
+
+TEST(QuatTest, SlerpOppositeAngles) {
+  Vector3dF axis(1, 1, 1);
+  double start_radians = -M_PI_2;
+  double stop_radians = M_PI_2;
+  Quaternion start(axis, start_radians);
+  Quaternion stop(axis, stop_radians);
+
+  // When quaternions are pointed in the fully opposite direction, we take the
+  // interpolated quaternion to be the first. This is arbitrary, but if we
+  // change this policy, this test should fail.
+  Quaternion expected = start;
+
+  for (size_t i = 0; i < 100; ++i) {
+    float t = static_cast<float>(i) / 100.0f;
+    Quaternion interpolated = start.Slerp(stop, t);
+    EXPECT_FLOAT_EQ(expected.x(), interpolated.x());
+    EXPECT_FLOAT_EQ(expected.y(), interpolated.y());
+    EXPECT_FLOAT_EQ(expected.z(), interpolated.z());
+    EXPECT_FLOAT_EQ(expected.w(), interpolated.w());
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/interpolated_transform.cc b/ui/gfx/interpolated_transform.cc
index fe367f9..8cf04d25 100644
--- a/ui/gfx/interpolated_transform.cc
+++ b/ui/gfx/interpolated_transform.cc
@@ -364,12 +364,8 @@
 
 gfx::Transform
 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
-  gfx::DecomposedTransform blended;
-  bool success = gfx::BlendDecomposedTransforms(&blended,
-                                                end_decomp_,
-                                                start_decomp_,
-                                                t);
-  DCHECK(success);
+  gfx::DecomposedTransform blended =
+      gfx::BlendDecomposedTransforms(end_decomp_, start_decomp_, t);
   return gfx::ComposeTransform(blended);
 }
 
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 97b6c8f..5cf7737 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/i18n/break_iterator.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -105,11 +106,11 @@
 
 // Creates a SkShader to fade the text, with |left_part| specifying the left
 // fade effect, if any, and |right_part| specifying the right fade effect.
-sk_sp<SkShader> CreateFadeShader(const FontList& font_list,
-                                 const Rect& text_rect,
-                                 const Rect& left_part,
-                                 const Rect& right_part,
-                                 SkColor color) {
+std::unique_ptr<cc::PaintShader> CreateFadeShader(const FontList& font_list,
+                                                  const Rect& text_rect,
+                                                  const Rect& left_part,
+                                                  const Rect& right_part,
+                                                  SkColor color) {
   // The shader should only specify transparency of the fade itself, not the
   // original transparency, which will be applied by the actual renderer.
   DCHECK_EQ(SkColorGetA(color), static_cast<uint8_t>(0xff));
@@ -144,9 +145,9 @@
 
   const SkPoint points[2] = { PointToSkPoint(text_rect.origin()),
                               PointToSkPoint(text_rect.top_right()) };
-  return
-      SkGradientShader::MakeLinear(&points[0], &colors[0], &positions[0],
-                                   colors.size(), SkShader::kClamp_TileMode);
+  return cc::PaintShader::MakeLinearGradient(&points[0], &colors[0],
+                                             &positions[0], colors.size(),
+                                             SkShader::kClamp_TileMode);
 }
 
 // Converts a FontRenderParams::Hinting value to the corresponding
@@ -231,8 +232,8 @@
   flags_.setColor(foreground);
 }
 
-void SkiaTextRenderer::SetShader(sk_sp<SkShader> shader) {
-  flags_.setShader(cc::WrapSkShader(std::move(shader)));
+void SkiaTextRenderer::SetShader(std::unique_ptr<cc::PaintShader> shader) {
+  flags_.setShader(std::move(shader));
 }
 
 void SkiaTextRenderer::SetUnderlineMetrics(SkScalar thickness,
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 00fe00c..5353be69 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -37,7 +37,6 @@
 
 class SkDrawLooper;
 struct SkPoint;
-class SkShader;
 class SkTypeface;
 
 namespace gfx {
@@ -63,7 +62,7 @@
   void SetTypeface(sk_sp<SkTypeface> typeface);
   void SetTextSize(SkScalar size);
   void SetForegroundColor(SkColor foreground);
-  void SetShader(sk_sp<SkShader> shader);
+  void SetShader(std::unique_ptr<cc::PaintShader> shader);
   // Sets underline metrics to use if the text will be drawn with an underline.
   // If not set, default values based on the size of the text will be used. The
   // two metrics must be set together.
diff --git a/ui/gfx/skia_paint_util.cc b/ui/gfx/skia_paint_util.cc
index dcaea71b..6bbe1ca 100644
--- a/ui/gfx/skia_paint_util.cc
+++ b/ui/gfx/skia_paint_util.cc
@@ -4,6 +4,7 @@
 
 #include "ui/gfx/skia_paint_util.h"
 
+#include "base/memory/ptr_util.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/effects/SkBlurMaskFilter.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
@@ -12,16 +13,17 @@
 
 namespace gfx {
 
-sk_sp<cc::PaintShader> CreateImageRepShader(const gfx::ImageSkiaRep& image_rep,
-                                            cc::PaintShader::TileMode tile_mode,
-                                            const SkMatrix& local_matrix) {
-  return cc::WrapSkShader(CreateImageRepShaderForScale(
-      image_rep, tile_mode, local_matrix, image_rep.scale()));
+std::unique_ptr<cc::PaintShader> CreateImageRepShader(
+    const gfx::ImageSkiaRep& image_rep,
+    SkShader::TileMode tile_mode,
+    const SkMatrix& local_matrix) {
+  return CreateImageRepShaderForScale(image_rep, tile_mode, local_matrix,
+                                      image_rep.scale());
 }
 
-sk_sp<cc::PaintShader> CreateImageRepShaderForScale(
+std::unique_ptr<cc::PaintShader> CreateImageRepShaderForScale(
     const gfx::ImageSkiaRep& image_rep,
-    cc::PaintShader::TileMode tile_mode,
+    SkShader::TileMode tile_mode,
     const SkMatrix& local_matrix,
     SkScalar scale) {
   // Unscale matrix by |scale| such that the bitmap is drawn at the
@@ -36,21 +38,22 @@
   shader_scale.setScaleX(local_matrix.getScaleX() / scale);
   shader_scale.setScaleY(local_matrix.getScaleY() / scale);
 
-  return cc::PaintShader::MakeBitmapShader(image_rep.sk_bitmap(), tile_mode,
-                                           tile_mode, &shader_scale);
+  return cc::PaintShader::MakeImage(
+      SkImage::MakeFromBitmap(image_rep.sk_bitmap()), tile_mode, tile_mode,
+      &shader_scale);
 }
 
-sk_sp<cc::PaintShader> CreateGradientShader(int start_point,
-                                            int end_point,
-                                            SkColor start_color,
-                                            SkColor end_color) {
+std::unique_ptr<cc::PaintShader> CreateGradientShader(int start_point,
+                                                      int end_point,
+                                                      SkColor start_color,
+                                                      SkColor end_color) {
   SkColor grad_colors[2] = {start_color, end_color};
   SkPoint grad_points[2];
   grad_points[0].iset(0, start_point);
   grad_points[1].iset(0, end_point);
 
-  return cc::WrapSkShader(SkGradientShader::MakeLinear(
-      grad_points, grad_colors, NULL, 2, cc::PaintShader::kClamp_TileMode));
+  return cc::PaintShader::MakeLinearGradient(grad_points, grad_colors, nullptr,
+                                             2, SkShader::kClamp_TileMode);
 }
 
 // This is copied from
diff --git a/ui/gfx/skia_paint_util.h b/ui/gfx/skia_paint_util.h
index f7e5d82d..1d163437 100644
--- a/ui/gfx/skia_paint_util.h
+++ b/ui/gfx/skia_paint_util.h
@@ -5,6 +5,7 @@
 #ifndef UI_GFX_SKIA_PAINT_UTIL_H_
 #define UI_GFX_SKIA_PAINT_UTIL_H_
 
+#include <memory>
 #include <vector>
 
 #include "cc/paint/paint_shader.h"
@@ -25,24 +26,25 @@
 // TODO(pkotwicz): Allow shader's local matrix to be changed after the shader
 // is created.
 //
-GFX_EXPORT sk_sp<cc::PaintShader> CreateImageRepShader(
+GFX_EXPORT std::unique_ptr<cc::PaintShader> CreateImageRepShader(
     const gfx::ImageSkiaRep& image_rep,
-    cc::PaintShader::TileMode tile_mode,
+    SkShader::TileMode tile_mode,
     const SkMatrix& local_matrix);
 
 // Creates a bitmap shader for the image rep with the passed in scale factor.
-GFX_EXPORT sk_sp<cc::PaintShader> CreateImageRepShaderForScale(
+GFX_EXPORT std::unique_ptr<cc::PaintShader> CreateImageRepShaderForScale(
     const gfx::ImageSkiaRep& image_rep,
-    cc::PaintShader::TileMode tile_mode,
+    SkShader::TileMode tile_mode,
     const SkMatrix& local_matrix,
     SkScalar scale);
 
 // Creates a vertical gradient shader. The caller owns the shader.
 // Example usage to avoid leaks:
-GFX_EXPORT sk_sp<cc::PaintShader> CreateGradientShader(int start_point,
-                                                       int end_point,
-                                                       SkColor start_color,
-                                                       SkColor end_color);
+GFX_EXPORT std::unique_ptr<cc::PaintShader> CreateGradientShader(
+    int start_point,
+    int end_point,
+    SkColor start_color,
+    SkColor end_color);
 
 // Creates a draw looper to generate |shadows|. The caller owns the draw looper.
 // NULL is returned if |shadows| is empty since no draw looper is needed in
diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc
index 9c8a0053..6e6aa91 100644
--- a/ui/gfx/transform.cc
+++ b/ui/gfx/transform.cc
@@ -14,6 +14,7 @@
 #include "ui/gfx/geometry/box_f.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector3d_f.h"
@@ -100,6 +101,25 @@
   matrix_.set(1, 3, y_translation);
 }
 
+Transform::Transform(const Quaternion& q)
+    : matrix_(SkMatrix44::kUninitialized_Constructor) {
+  double x = q.x();
+  double y = q.y();
+  double z = q.z();
+  double w = q.w();
+
+  // Implicitly calls matrix.setIdentity()
+  matrix_.set3x3(SkDoubleToMScalar(1.0 - 2.0 * (y * y + z * z)),
+                 SkDoubleToMScalar(2.0 * (x * y + z * w)),
+                 SkDoubleToMScalar(2.0 * (x * z - y * w)),
+                 SkDoubleToMScalar(2.0 * (x * y - z * w)),
+                 SkDoubleToMScalar(1.0 - 2.0 * (x * x + z * z)),
+                 SkDoubleToMScalar(2.0 * (y * z + x * w)),
+                 SkDoubleToMScalar(2.0 * (x * z + y * w)),
+                 SkDoubleToMScalar(2.0 * (y * z - x * w)),
+                 SkDoubleToMScalar(1.0 - 2.0 * (x * x + y * y)));
+}
+
 void Transform::RotateAboutXAxis(double degrees) {
   double radians = degrees * M_PI / 180;
   SkMScalar cosTheta = SkDoubleToMScalar(std::cos(radians));
@@ -506,8 +526,7 @@
       !DecomposeTransform(&from_decomp, from))
     return false;
 
-  if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
-    return false;
+  to_decomp = BlendDecomposedTransforms(to_decomp, from_decomp, progress);
 
   matrix_ = ComposeTransform(to_decomp).matrix();
   return true;
diff --git a/ui/gfx/transform.h b/ui/gfx/transform.h
index a942bd6c..0964f74 100644
--- a/ui/gfx/transform.h
+++ b/ui/gfx/transform.h
@@ -19,6 +19,7 @@
 class RectF;
 class Point;
 class Point3F;
+class Quaternion;
 class Vector3dF;
 
 // 4x4 transformation matrix. Transform is cheap and explicitly allows
@@ -69,6 +70,9 @@
             SkMScalar x_translation,
             SkMScalar y_translation);
 
+  // Constructs a transform corresponding to the given quaternion.
+  explicit Transform(const Quaternion& q);
+
   bool operator==(const Transform& rhs) const { return matrix_ == rhs.matrix_; }
   bool operator!=(const Transform& rhs) const { return matrix_ != rhs.matrix_; }
 
diff --git a/ui/gfx/transform_unittest.cc b/ui/gfx/transform_unittest.cc
index 136f4198..b1b880a 100644
--- a/ui/gfx/transform_unittest.cc
+++ b/ui/gfx/transform_unittest.cc
@@ -745,13 +745,8 @@
       // A 180 degree rotation is exactly opposite on the sphere, therefore
       // either great circle arc to it is equivalent (and numerical precision
       // will determine which is closer).  Test both directions.
-      Transform expected1;
-      expected1.RotateAbout(axes[index], 180.0 * t);
-      Transform expected2;
-      expected2.RotateAbout(axes[index], -180.0 * t);
-
-      EXPECT_TRUE(MatricesAreNearlyEqual(expected1, to) ||
-                  MatricesAreNearlyEqual(expected2, to))
+      Transform expected = from;
+      EXPECT_TRUE(MatricesAreNearlyEqual(expected, to))
           << "axis: " << index << ", i: " << i;
     }
   }
@@ -972,36 +967,41 @@
   to = Transform();
   to.Skew(0.0, 45.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_NEAR(1.0823489449280947471976333,
-                   0.0464370719145053845178239,
-                   0.0,
-                   0.0,
-                   to,
-                   LOOSE_ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.2152925909665224513123150,
-                   0.9541702441750861130032035,
-                   0.0,
-                   0.0,
-                   to,
-                   LOOSE_ERROR_THRESHOLD);
+  EXPECT_LT(1.0, to.matrix().get(0, 0));
+  EXPECT_GT(1.5, to.matrix().get(0, 0));
+  EXPECT_LT(0.0, to.matrix().get(0, 1));
+  EXPECT_GT(0.5, to.matrix().get(0, 1));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 2));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 3));
+
+  EXPECT_LT(0.0, to.matrix().get(1, 0));
+  EXPECT_GT(0.5, to.matrix().get(1, 0));
+  EXPECT_LT(0.0, to.matrix().get(1, 1));
+  EXPECT_GT(1.0, to.matrix().get(1, 1));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 2));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 3));
+
   EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
   EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Skew(0.0, 45.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_NEAR(1.1152212925809066312865525,
-                   0.0676495144007326631996335,
-                   0.0,
-                   0.0,
-                   to,
-                   LOOSE_ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.4619397844342648662419037,
-                   0.9519009045724774464858342,
-                   0.0,
-                   0.0,
-                   to,
-                   LOOSE_ERROR_THRESHOLD);
+
+  EXPECT_LT(1.0, to.matrix().get(0, 0));
+  EXPECT_GT(1.5, to.matrix().get(0, 0));
+  EXPECT_LT(0.0, to.matrix().get(0, 1));
+  EXPECT_GT(0.5, to.matrix().get(0, 1));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 2));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 3));
+
+  EXPECT_LT(0.0, to.matrix().get(1, 0));
+  EXPECT_GT(1.0, to.matrix().get(1, 0));
+  EXPECT_LT(0.0, to.matrix().get(1, 1));
+  EXPECT_GT(1.0, to.matrix().get(1, 1));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 2));
+  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 3));
+
   EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
   EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
@@ -1270,11 +1270,15 @@
     EXPECT_EQ(0.0, decomp.translate[i]);
     EXPECT_EQ(1.0, decomp.scale[i]);
     EXPECT_EQ(0.0, decomp.skew[i]);
-    EXPECT_EQ(0.0, decomp.quaternion[i]);
     EXPECT_EQ(0.0, decomp.perspective[i]);
   }
-  EXPECT_EQ(1.0, decomp.quaternion[3]);
   EXPECT_EQ(1.0, decomp.perspective[3]);
+
+  EXPECT_EQ(0.0, decomp.quaternion.x());
+  EXPECT_EQ(0.0, decomp.quaternion.y());
+  EXPECT_EQ(0.0, decomp.quaternion.z());
+  EXPECT_EQ(1.0, decomp.quaternion.w());
+
   Transform identity;
   Transform composed = ComposeTransform(decomp);
   EXPECT_TRUE(MatricesAreNearlyEqual(identity, composed));
@@ -1295,7 +1299,7 @@
     EXPECT_FLOAT_EQ(decomp.translate[0], degrees * 2);
     EXPECT_FLOAT_EQ(decomp.translate[1], -degrees * 3);
     double rotation =
-        std::acos(SkMScalarToDouble(decomp.quaternion[3])) * 360.0 / M_PI;
+        std::acos(SkMScalarToDouble(decomp.quaternion.w())) * 360.0 / M_PI;
     while (rotation < 0.0)
       rotation += 360.0;
     while (rotation > 360.0)
diff --git a/ui/gfx/transform_util.cc b/ui/gfx/transform_util.cc
index 96e02724..2dd322fa 100644
--- a/ui/gfx/transform_util.cc
+++ b/ui/gfx/transform_util.cc
@@ -56,46 +56,6 @@
   return SkDoubleToMScalar(std::floor(SkMScalarToDouble(n) + 0.5));
 }
 
-// Taken from http://www.w3.org/TR/css3-transforms/.
-bool Slerp(SkMScalar out[4],
-           const SkMScalar q1[4],
-           const SkMScalar q2[4],
-           double progress) {
-  double product = Dot<4>(q1, q2);
-
-  // Clamp product to -1.0 <= product <= 1.0.
-  product = std::min(std::max(product, -1.0), 1.0);
-
-  const double epsilon = 1e-5;
-  if (std::abs(product - 1.0) < epsilon) {
-    for (int i = 0; i < 4; ++i)
-      out[i] = q1[i];
-    return true;
-  }
-
-  // TODO(vmpstr): In case the product is -1, the vectors are exactly opposite
-  // of each other. In this case, it's technically not correct to just pick one
-  // of the vectors, we instead need to pick how to interpolate. However, the
-  // spec isn't clear on this. If we don't handle the -1 case explicitly, it
-  // results in inf and nans however, which is worse. See crbug.com/506543 for
-  // more discussion.
-  if (std::abs(product + 1.0) < epsilon) {
-    for (int i = 0; i < 4; ++i)
-      out[i] = q1[i];
-    return true;
-  }
-
-  double denom = std::sqrt(1.0 - product * product);
-  double theta = std::acos(product);
-  double w = std::sin(progress * theta) * (1.0 / denom);
-
-  double scale1 = std::cos(progress * theta) - product * w;
-  double scale2 = w;
-  Combine<4>(out, q1, q2, scale1, scale2);
-
-  return true;
-}
-
 // Returns false if the matrix cannot be normalized.
 bool Normalize(SkMatrix44& m) {
   if (m.get(3, 3) == 0.0)
@@ -135,24 +95,7 @@
 }
 
 SkMatrix44 BuildRotationMatrix(const DecomposedTransform& decomp) {
-  double x = decomp.quaternion[0];
-  double y = decomp.quaternion[1];
-  double z = decomp.quaternion[2];
-  double w = decomp.quaternion[3];
-
-  SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
-
-  // Implicitly calls matrix.setIdentity()
-  matrix.set3x3(SkDoubleToMScalar(1.0 - 2.0 * (y * y + z * z)),
-                SkDoubleToMScalar(2.0 * (x * y + z * w)),
-                SkDoubleToMScalar(2.0 * (x * z - y * w)),
-                SkDoubleToMScalar(2.0 * (x * y - z * w)),
-                SkDoubleToMScalar(1.0 - 2.0 * (x * x + z * z)),
-                SkDoubleToMScalar(2.0 * (y * z + x * w)),
-                SkDoubleToMScalar(2.0 * (x * z + y * w)),
-                SkDoubleToMScalar(2.0 * (y * z - x * w)),
-                SkDoubleToMScalar(1.0 - 2.0 * (x * x + y * y)));
-  return matrix;
+  return Transform(decomp.quaternion).matrix();
 }
 
 SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
@@ -282,22 +225,21 @@
   scale[0] = scale[1] = scale[2] = 1.0;
   skew[0] = skew[1] = skew[2] = 0.0;
   perspective[0] = perspective[1] = perspective[2] = 0.0;
-  quaternion[0] = quaternion[1] = quaternion[2] = 0.0;
-  perspective[3] = quaternion[3] = 1.0;
+  perspective[3] = 1.0;
 }
 
-bool BlendDecomposedTransforms(DecomposedTransform* out,
-                               const DecomposedTransform& to,
-                               const DecomposedTransform& from,
-                               double progress) {
+DecomposedTransform BlendDecomposedTransforms(const DecomposedTransform& to,
+                                              const DecomposedTransform& from,
+                                              double progress) {
+  DecomposedTransform out;
   double scalea = progress;
   double scaleb = 1.0 - progress;
-  Combine<3>(out->translate, to.translate, from.translate, scalea, scaleb);
-  Combine<3>(out->scale, to.scale, from.scale, scalea, scaleb);
-  Combine<3>(out->skew, to.skew, from.skew, scalea, scaleb);
-  Combine<4>(
-      out->perspective, to.perspective, from.perspective, scalea, scaleb);
-  return Slerp(out->quaternion, from.quaternion, to.quaternion, progress);
+  Combine<3>(out.translate, to.translate, from.translate, scalea, scaleb);
+  Combine<3>(out.scale, to.scale, from.scale, scalea, scaleb);
+  Combine<3>(out.skew, to.skew, from.skew, scalea, scaleb);
+  Combine<4>(out.perspective, to.perspective, from.perspective, scalea, scaleb);
+  out.quaternion = from.quaternion.Slerp(to.quaternion, progress);
+  return out;
 }
 
 // Taken from http://www.w3.org/TR/css3-transforms/.
@@ -420,21 +362,22 @@
   double row00 = SkMScalarToDouble(row[0][0]);
   double row11 = SkMScalarToDouble(row[1][1]);
   double row22 = SkMScalarToDouble(row[2][2]);
-  decomp->quaternion[0] = SkDoubleToMScalar(
-      0.5 * std::sqrt(std::max(1.0 + row00 - row11 - row22, 0.0)));
-  decomp->quaternion[1] = SkDoubleToMScalar(
-      0.5 * std::sqrt(std::max(1.0 - row00 + row11 - row22, 0.0)));
-  decomp->quaternion[2] = SkDoubleToMScalar(
-      0.5 * std::sqrt(std::max(1.0 - row00 - row11 + row22, 0.0)));
-  decomp->quaternion[3] = SkDoubleToMScalar(
-      0.5 * std::sqrt(std::max(1.0 + row00 + row11 + row22, 0.0)));
+
+  decomp->quaternion.set_x(SkDoubleToMScalar(
+      0.5 * std::sqrt(std::max(1.0 + row00 - row11 - row22, 0.0))));
+  decomp->quaternion.set_y(SkDoubleToMScalar(
+      0.5 * std::sqrt(std::max(1.0 - row00 + row11 - row22, 0.0))));
+  decomp->quaternion.set_z(SkDoubleToMScalar(
+      0.5 * std::sqrt(std::max(1.0 - row00 - row11 + row22, 0.0))));
+  decomp->quaternion.set_w(SkDoubleToMScalar(
+      0.5 * std::sqrt(std::max(1.0 + row00 + row11 + row22, 0.0))));
 
   if (row[2][1] > row[1][2])
-      decomp->quaternion[0] = -decomp->quaternion[0];
+    decomp->quaternion.set_x(-decomp->quaternion.x());
   if (row[0][2] > row[2][0])
-      decomp->quaternion[1] = -decomp->quaternion[1];
+    decomp->quaternion.set_y(-decomp->quaternion.y());
   if (row[1][0] > row[0][1])
-      decomp->quaternion[2] = -decomp->quaternion[2];
+    decomp->quaternion.set_z(-decomp->quaternion.z());
 
   return true;
 }
@@ -495,23 +438,10 @@
       "skew: %+0.4f %+0.4f %+0.4f\n"
       "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
       "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
-      translate[0],
-      translate[1],
-      translate[2],
-      scale[0],
-      scale[1],
-      scale[2],
-      skew[0],
-      skew[1],
-      skew[2],
-      perspective[0],
-      perspective[1],
-      perspective[2],
-      perspective[3],
-      quaternion[0],
-      quaternion[1],
-      quaternion[2],
-      quaternion[3]);
+      translate[0], translate[1], translate[2], scale[0], scale[1], scale[2],
+      skew[0], skew[1], skew[2], perspective[0], perspective[1], perspective[2],
+      perspective[3], quaternion.x(), quaternion.y(), quaternion.z(),
+      quaternion.w());
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/transform_util.h b/ui/gfx/transform_util.h
index 597d877..78b7739 100644
--- a/ui/gfx/transform_util.h
+++ b/ui/gfx/transform_util.h
@@ -6,6 +6,7 @@
 #define UI_GFX_TRANSFORM_UTIL_H_
 
 #include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/transform.h"
 
@@ -28,7 +29,7 @@
   SkMScalar scale[3];
   SkMScalar skew[3];
   SkMScalar perspective[4];
-  SkMScalar quaternion[4];
+  Quaternion quaternion;
 
   std::string ToString() const;
 
@@ -37,12 +38,12 @@
 
 // Interpolates the decomposed components |to| with |from| using the
 // routines described in http://www.w3.org/TR/css3-3d-transform/.
-// |progress| is in the range [0, 1] (0 leaves |out| unchanged, and 1
-// assigns |from| to |out|).
-GFX_EXPORT bool BlendDecomposedTransforms(DecomposedTransform* out,
-                                          const DecomposedTransform& to,
-                                          const DecomposedTransform& from,
-                                          double progress);
+// |progress| is in the range [0, 1]. If 0 we will return |from|, if 1, we will
+// return |to|.
+GFX_EXPORT DecomposedTransform
+BlendDecomposedTransforms(const DecomposedTransform& to,
+                          const DecomposedTransform& from,
+                          double progress);
 
 // Decomposes this transform into its translation, scale, skew, perspective,
 // and rotation components following the routines detailed in this spec:
diff --git a/ui/gfx/transform_util_unittest.cc b/ui/gfx/transform_util_unittest.cc
index 907f8a8..c9fb6d1 100644
--- a/ui/gfx/transform_util_unittest.cc
+++ b/ui/gfx/transform_util_unittest.cc
@@ -197,14 +197,19 @@
 TEST(TransformUtilTest, BlendOppositeQuaternions) {
   DecomposedTransform first;
   DecomposedTransform second;
-  second.quaternion[3] = -second.quaternion[3];
+  second.quaternion.set_w(-second.quaternion.w());
 
-  DecomposedTransform result;
-  BlendDecomposedTransforms(&result, first, second, 0.25);
-  for (size_t i = 0; i < 4; ++i) {
-    EXPECT_TRUE(std::isfinite(result.quaternion[i]));
-    EXPECT_FALSE(std::isnan(result.quaternion[i]));
-  }
+  DecomposedTransform result = BlendDecomposedTransforms(first, second, 0.25);
+
+  EXPECT_TRUE(std::isfinite(result.quaternion.x()));
+  EXPECT_TRUE(std::isfinite(result.quaternion.y()));
+  EXPECT_TRUE(std::isfinite(result.quaternion.z()));
+  EXPECT_TRUE(std::isfinite(result.quaternion.w()));
+
+  EXPECT_FALSE(std::isnan(result.quaternion.x()));
+  EXPECT_FALSE(std::isnan(result.quaternion.y()));
+  EXPECT_FALSE(std::isnan(result.quaternion.z()));
+  EXPECT_FALSE(std::isnan(result.quaternion.w()));
 }
 
 }  // namespace
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 5563fe2..3a44167 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -109,6 +109,11 @@
 #define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
 #endif /* EGL_ANGLE_flexible_surface_compatibility */
 
+#ifndef EGL_ANGLE_display_robust_resource_initialization
+#define EGL_ANGLE_display_robust_resource_initialization 1
+#define EGL_DISPLAY_ROBUST_RESOURCE_INITIALIZATION_ANGLE 0x3453
+#endif /* EGL_ANGLE_display_robust_resource_initialization */
+
 using ui::GetLastEGLErrorString;
 
 namespace gl {
@@ -173,7 +178,8 @@
 
 EGLDisplay GetPlatformANGLEDisplay(EGLNativeDisplayType native_display,
                                    EGLenum platform_type,
-                                   bool warpDevice) {
+                                   bool warpDevice,
+                                   bool robustResourceInit) {
   std::vector<EGLint> display_attribs;
 
   display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
@@ -184,6 +190,11 @@
     display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE);
   }
 
+  if (robustResourceInit) {
+    display_attribs.push_back(EGL_DISPLAY_ROBUST_RESOURCE_INITIALIZATION_ANGLE);
+    display_attribs.push_back(EGL_TRUE);
+  }
+
 #if defined(USE_X11) && !defined(OS_CHROMEOS)
   // ANGLE_NULL doesn't use the visual, and may run without X11 where we can't
   // get it anyway.
@@ -204,26 +215,32 @@
 }
 
 EGLDisplay GetDisplayFromType(DisplayType display_type,
-                              EGLNativeDisplayType native_display) {
+                              EGLNativeDisplayType native_display,
+                              bool robustResourceInit) {
   switch (display_type) {
     case DEFAULT:
     case SWIFT_SHADER:
       return eglGetDisplay(native_display);
     case ANGLE_D3D9:
       return GetPlatformANGLEDisplay(native_display,
-                                     EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, false);
+                                     EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, false,
+                                     robustResourceInit);
     case ANGLE_D3D11:
-      return GetPlatformANGLEDisplay(
-          native_display, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, false);
+      return GetPlatformANGLEDisplay(native_display,
+                                     EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, false,
+                                     robustResourceInit);
     case ANGLE_OPENGL:
-      return GetPlatformANGLEDisplay(
-          native_display, EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, false);
+      return GetPlatformANGLEDisplay(native_display,
+                                     EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE,
+                                     false, robustResourceInit);
     case ANGLE_OPENGLES:
-      return GetPlatformANGLEDisplay(
-          native_display, EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, false);
+      return GetPlatformANGLEDisplay(native_display,
+                                     EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE,
+                                     false, robustResourceInit);
     case ANGLE_NULL:
       return GetPlatformANGLEDisplay(native_display,
-                                     EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE, false);
+                                     EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE, false,
+                                     robustResourceInit);
     default:
       NOTREACHED();
       return EGL_NO_DISPLAY;
@@ -680,6 +697,15 @@
         ExtensionsContain(client_extensions, "EGL_ANGLE_platform_angle_null");
   }
 
+  bool supports_robust_resource_init =
+      client_extensions &&
+      ExtensionsContain(client_extensions,
+                        "EGL_ANGLE_display_robust_resource_initialization");
+  bool use_robust_resource_init =
+      supports_robust_resource_init &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUsePassthroughCmdDecoderGL);
+
   std::vector<DisplayType> init_displays;
   GetEGLInitDisplays(supports_angle_d3d, supports_angle_opengl,
                      supports_angle_null,
@@ -687,8 +713,8 @@
 
   for (size_t disp_index = 0; disp_index < init_displays.size(); ++disp_index) {
     DisplayType display_type = init_displays[disp_index];
-    EGLDisplay display =
-        GetDisplayFromType(display_type, g_native_display);
+    EGLDisplay display = GetDisplayFromType(display_type, g_native_display,
+                                            use_robust_resource_init);
     if (display == EGL_NO_DISPLAY) {
       LOG(ERROR) << "EGL display query failed with error "
                  << GetLastEGLErrorString();
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc
index ba72048a..27f49c5 100644
--- a/ui/gl/gl_switches.cc
+++ b/ui/gl/gl_switches.cc
@@ -122,6 +122,11 @@
 const char kDisableDirectCompositionLayers[] =
     "disable-direct-composition-layers";
 
+// Use the Pass-through command decoder, skipping all validation and state
+// tracking. Note: This is the same switch as the one in gpu_switches.cc. It's
+// defined here again to avoid dependencies between dlls.
+const char kUsePassthroughCmdDecoderGL[] = "use-passthrough-cmd-decoder";
+
 // This is the list of switches passed from this file that are passed from the
 // GpuProcessHost to the GPU Process. Add your switch to this list if you need
 // to read it in the GPU process, else don't add it.
@@ -139,6 +144,7 @@
     kEnableSwapBuffersWithBounds,
     kEnableDirectCompositionLayers,
     kDisableDirectCompositionLayers,
+    kUsePassthroughCmdDecoderGL,
 };
 const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches =
     arraysize(kGLSwitchesCopiedFromGpuProcessHost);
diff --git a/ui/gl/gl_switches.h b/ui/gl/gl_switches.h
index 3a1948b..4d6cf34 100644
--- a/ui/gl/gl_switches.h
+++ b/ui/gl/gl_switches.h
@@ -57,6 +57,8 @@
 GL_EXPORT extern const char kEnableDirectCompositionLayers[];
 GL_EXPORT extern const char kDisableDirectCompositionLayers[];
 
+GL_EXPORT extern const char kUsePassthroughCmdDecoderGL[];
+
 // These flags are used by the test harness code, not passed in by users.
 GL_EXPORT extern const char kDisableGLDrawingForTests[];
 GL_EXPORT extern const char kOverrideUseSoftwareGLForTests[];
diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc
index d36a2c31..6711ce93 100644
--- a/ui/native_theme/native_theme_base.cc
+++ b/ui/native_theme/native_theme_base.cc
@@ -600,11 +600,11 @@
   SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]};
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
-  flags.setShader(cc::WrapSkShader(SkGradientShader::MakeLinear(
-      gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode)));
+  flags.setShader(cc::PaintShader::MakeLinearGradient(
+      gradient_bounds, colors, nullptr, 3, SkShader::kClamp_TileMode));
   flags.setStyle(cc::PaintFlags::kFill_Style);
   canvas->drawRoundRect(skrect, borderRadius, borderRadius, flags);
-  flags.setShader(NULL);
+  flags.setShader(nullptr);
 
   // Draw the border.
   if (state == kHovered)
@@ -681,11 +681,11 @@
 
   flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setAntiAlias(true);
-  flags.setShader(cc::WrapSkShader(SkGradientShader::MakeLinear(
-      gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode)));
+  flags.setShader(cc::PaintShader::MakeLinearGradient(
+      gradient_bounds, colors, nullptr, 2, SkShader::kClamp_TileMode));
 
   canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), flags);
-  flags.setShader(NULL);
+  flags.setShader(nullptr);
 
   if (button.has_border) {
     int border_alpha = state == kHovered ? 0x80 : 0x55;
diff --git a/ui/ozone/public/input_controller.cc b/ui/ozone/public/input_controller.cc
index 665472d4..5aa3002 100644
--- a/ui/ozone/public/input_controller.cc
+++ b/ui/ozone/public/input_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ui/ozone/public/input_controller.h"
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -30,7 +31,7 @@
                          const base::TimeDelta& interval) override;
   void GetAutoRepeatRate(base::TimeDelta* delay,
                          base::TimeDelta* interval) override;
-  bool SetCurrentLayoutByName(const std::string& layout_name) override;
+  void SetCurrentLayoutByName(const std::string& layout_name) override;
   void SetTouchEventLoggingEnabled(bool enabled) override;
   void SetTouchpadSensitivity(int value) override;
   void SetTapToClick(bool enabled) override;
@@ -40,9 +41,9 @@
   void SetMouseSensitivity(int value) override;
   void SetPrimaryButtonRight(bool right) override;
   void SetTapToClickPaused(bool state) override;
-  void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) override;
+  void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override;
   void GetTouchEventLog(const base::FilePath& out_dir,
-                        const GetTouchEventLogReply& reply) override;
+                        GetTouchEventLogReply reply) override;
   void SetInternalTouchpadEnabled(bool enabled) override;
   bool IsInternalTouchpadEnabled() const override;
   void SetTouchscreensEnabled(bool enabled) override;
@@ -92,10 +93,8 @@
                                             base::TimeDelta* interval) {
 }
 
-bool StubInputController::SetCurrentLayoutByName(
-    const std::string& layout_name) {
-  return false;
-}
+void StubInputController::SetCurrentLayoutByName(
+    const std::string& layout_name) {}
 
 void StubInputController::SetTouchpadSensitivity(int value) {
 }
@@ -126,13 +125,13 @@
 }
 
 void StubInputController::GetTouchDeviceStatus(
-    const GetTouchDeviceStatusReply& reply) {
-  reply.Run(std::unique_ptr<std::string>(new std::string));
+    GetTouchDeviceStatusReply reply) {
+  std::move(reply).Run(std::string());
 }
 
 void StubInputController::GetTouchEventLog(const base::FilePath& out_dir,
-                                           const GetTouchEventLogReply& reply) {
-  reply.Run(base::WrapUnique(new std::vector<base::FilePath>));
+                                           GetTouchEventLogReply reply) {
+  std::move(reply).Run(std::vector<base::FilePath>());
 }
 
 void StubInputController::SetInternalTouchpadEnabled(bool enabled) {
diff --git a/ui/ozone/public/input_controller.h b/ui/ozone/public/input_controller.h
index 3c5e1bcb..cdec1d5 100644
--- a/ui/ozone/public/input_controller.h
+++ b/ui/ozone/public/input_controller.h
@@ -10,7 +10,7 @@
 #include <string>
 #include <vector>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "ui/ozone/ozone_base_export.h"
@@ -30,10 +30,11 @@
 // script that is originally located at /opt/google/chrome/.
 class OZONE_BASE_EXPORT InputController {
  public:
-  typedef base::Callback<void(std::unique_ptr<std::string>)>
-      GetTouchDeviceStatusReply;
-  typedef base::Callback<void(std::unique_ptr<std::vector<base::FilePath>>)>
-      GetTouchEventLogReply;
+  using GetTouchDeviceStatusReply =
+      base::OnceCallback<void(const std::string&)>;
+  // TODO(sky): convert this to value once mojo supports move for vectors.
+  using GetTouchEventLogReply =
+      base::OnceCallback<void(const std::vector<base::FilePath>&)>;
 
   InputController() {}
   virtual ~InputController() {}
@@ -52,7 +53,7 @@
                                  const base::TimeDelta& interval) = 0;
   virtual void GetAutoRepeatRate(base::TimeDelta* delay,
                                  base::TimeDelta* interval) = 0;
-  virtual bool SetCurrentLayoutByName(const std::string& layout_name) = 0;
+  virtual void SetCurrentLayoutByName(const std::string& layout_name) = 0;
 
   // Touchpad settings.
   virtual void SetTouchpadSensitivity(int value) = 0;
@@ -66,9 +67,9 @@
   virtual void SetPrimaryButtonRight(bool right) = 0;
 
   // Touch log collection.
-  virtual void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) = 0;
+  virtual void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) = 0;
   virtual void GetTouchEventLog(const base::FilePath& out_dir,
-                                const GetTouchEventLogReply& reply) = 0;
+                                GetTouchEventLogReply reply) = 0;
   // Touchscreen log settings.
   virtual void SetTouchEventLoggingEnabled(bool enabled) = 0;
 
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index 3eb3970..df8d0cf 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -105,8 +105,8 @@
   else
     points[1].iset(0, rect.height() + 1);
   cc::PaintFlags flags;
-  flags.setShader(cc::WrapSkShader(SkGradientShader::MakeLinear(
-      points, colors, NULL, 2, SkShader::kClamp_TileMode)));
+  flags.setShader(cc::PaintShader::MakeLinearGradient(
+      points, colors, nullptr, 2, SkShader::kClamp_TileMode));
   canvas->DrawRect(rect, flags);
 }
 
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index e9c1dd6..c7e4cd4ac 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -258,9 +258,9 @@
     gradient_bounds[1].set(track_rect.right(), track_rect.y());
   }
   cc::PaintFlags gradient;
-  gradient.setShader(cc::WrapSkShader(SkGradientShader::MakeLinear(
+  gradient.setShader(cc::PaintShader::MakeLinearGradient(
       gradient_bounds, kScrollerTrackGradientColors, nullptr,
-      arraysize(kScrollerTrackGradientColors), SkShader::kClamp_TileMode)));
+      arraysize(kScrollerTrackGradientColors), SkShader::kClamp_TileMode));
   canvas->DrawRect(track_rect, gradient);
 
   // Draw the inner border: top if horizontal, left if vertical.
diff --git a/ui/views/view.cc b/ui/views/view.cc
index e62c3951..6d1e20b 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1863,10 +1863,9 @@
                    decomp.translate[1]);
     result.append(bounds_buffer);
 
-    base::snprintf(bounds_buffer,
-                   arraysize(bounds_buffer),
+    base::snprintf(bounds_buffer, arraysize(bounds_buffer),
                    "\\n rotation: %3.2f",
-                   std::acos(decomp.quaternion[3]) * 360.0 / M_PI);
+                   std::acos(decomp.quaternion.w()) * 360.0 / M_PI);
     result.append(bounds_buffer);
 
     base::snprintf(bounds_buffer,