diff --git a/DEPS b/DEPS
index 3ce20e0e..9c94833 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '2937ab856e912e5c103bf13e7cdd1255d78a4cff',
+  'v8_revision': 'c2d2c94fd4960b69e0ea915b9e862f16bea778cb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '64ca2b5ad4d43754240682ba97b164dd72dad794',
+  'catapult_revision': 'b0acf6c12b290c7dcbb54b389f7120c580a8e39a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index fc07745..0a1d129 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2134,8 +2134,9 @@
 
 
 def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
-  macro_re = input_api.re.compile(
-      r'^\s*#(el)?if.*\bdefined\(((OS_|COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
+  # Excludes OS_CHROMEOS, which is not defined in build_config.h.
+  macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
+                                  'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
   include_re = input_api.re.compile(
       r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
   extension_re = input_api.re.compile(r'\.[a-z]+$')
diff --git a/android_webview/browser/aw_render_thread_context_provider.cc b/android_webview/browser/aw_render_thread_context_provider.cc
index a3ed6c8d..f89de782 100644
--- a/android_webview/browser/aw_render_thread_context_provider.cc
+++ b/android_webview/browser/aw_render_thread_context_provider.cc
@@ -9,8 +9,8 @@
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/trace_event/trace_event.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/output/managed_memory_policy.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/gles2_lib.h"
 #include "gpu/command_buffer/client/gles2_trace_implementation.h"
@@ -81,7 +81,7 @@
   }
 
   cache_controller_.reset(
-      new cc::ContextCacheController(context_->GetImplementation(), nullptr));
+      new viz::ContextCacheController(context_->GetImplementation(), nullptr));
 }
 
 AwRenderThreadContextProvider::~AwRenderThreadContextProvider() {
@@ -135,7 +135,7 @@
   return gr_context_.get();
 }
 
-cc::ContextCacheController* AwRenderThreadContextProvider::CacheController() {
+viz::ContextCacheController* AwRenderThreadContextProvider::CacheController() {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   return cache_controller_.get();
 }
diff --git a/android_webview/browser/aw_render_thread_context_provider.h b/android_webview/browser/aw_render_thread_context_provider.h
index 44301a31..ca2589c5 100644
--- a/android_webview/browser/aw_render_thread_context_provider.h
+++ b/android_webview/browser/aw_render_thread_context_provider.h
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/ipc/in_process_command_buffer.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/gpu/GrContext.h"
@@ -29,7 +29,7 @@
 
 namespace android_webview {
 
-class AwRenderThreadContextProvider : public cc::ContextProvider {
+class AwRenderThreadContextProvider : public viz::ContextProvider {
  public:
   static scoped_refptr<AwRenderThreadContextProvider> Create(
       scoped_refptr<gl::GLSurface> surface,
@@ -45,13 +45,13 @@
       scoped_refptr<gpu::InProcessCommandBuffer::Service> service);
   ~AwRenderThreadContextProvider() override;
 
-  // cc::ContextProvider:
+  // viz::ContextProvider:
   bool BindToCurrentThread() override;
   gpu::Capabilities ContextCapabilities() override;
   gpu::gles2::GLES2Interface* ContextGL() override;
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
-  cc::ContextCacheController* CacheController() override;
+  viz::ContextCacheController* CacheController() override;
   void InvalidateGrContext(uint32_t state) override;
   base::Lock* GetLock() override;
   void SetLostContextCallback(
@@ -64,7 +64,7 @@
   std::unique_ptr<gpu::GLInProcessContext> context_;
   std::unique_ptr<gpu::gles2::GLES2TraceImplementation> trace_impl_;
   sk_sp<class GrContext> gr_context_;
-  std::unique_ptr<cc::ContextCacheController> cache_controller_;
+  std::unique_ptr<viz::ContextCacheController> cache_controller_;
 
   LostContextCallback lost_context_callback_;
 
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 46029ea4..f4ad8144 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -33,7 +33,8 @@
       last_committed_layer_tree_frame_sink_id_(0u),
       last_submitted_layer_tree_frame_sink_id_(0u) {
   DCHECK(last_egl_context_);
-  surfaces_->GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
+  surfaces_->GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(
+      frame_sink_id_);
   CreateNewCompositorFrameSinkSupport();
 }
 
@@ -43,7 +44,8 @@
   if (child_id_.is_valid())
     DestroySurface();
   support_.reset();
-  surfaces_->GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
+  surfaces_->GetFrameSinkManager()->surface_manager()->InvalidateFrameSinkId(
+      frame_sink_id_);
 
   // Reset draw constraints.
   render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
diff --git a/ash/laser/DEPS b/ash/laser/DEPS
index 89980df..c592c5d 100644
--- a/ash/laser/DEPS
+++ b/ash/laser/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+cc",
+  "+components/viz/common/gpu",
   "+components/viz/common/quads",
   "+gpu/command_buffer/client",
 ]
diff --git a/ash/laser/laser_pointer_view.cc b/ash/laser/laser_pointer_view.cc
index 784f10c..b46de05 100644
--- a/ash/laser/laser_pointer_view.cc
+++ b/ash/laser/laser_pointer_view.cc
@@ -22,11 +22,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/output/compositor_frame.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink.h"
 #include "cc/output/layer_tree_frame_sink_client.h"
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/resources/transferable_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/quads/resource_format.h"
 #include "components/viz/common/quads/texture_mailbox.h"
 #include "gpu/command_buffer/client/context_support.h"
@@ -240,7 +240,7 @@
         gles2->DestroyImageCHROMIUM(image);
     }
   }
-  scoped_refptr<cc::ContextProvider> context_provider;
+  scoped_refptr<viz::ContextProvider> context_provider;
   uint32_t texture = 0;
   uint32_t image = 0;
   gpu::Mailbox mailbox;
diff --git a/ash/test/ash_test_suite.cc b/ash/test/ash_test_suite.cc
index 76c38033..56e5c8b0 100644
--- a/ash/test/ash_test_suite.cc
+++ b/ash/test/ash_test_suite.cc
@@ -32,11 +32,12 @@
 class FrameSinkClient : public viz::TestLayerTreeFrameSinkClient {
  public:
   explicit FrameSinkClient(
-      scoped_refptr<cc::ContextProvider> display_context_provider)
+      scoped_refptr<viz::ContextProvider> display_context_provider)
       : display_context_provider_(std::move(display_context_provider)) {}
 
   std::unique_ptr<cc::OutputSurface> CreateDisplayOutputSurface(
-      scoped_refptr<cc::ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     return cc::FakeOutputSurface::Create3d(
         std::move(display_context_provider_));
   }
@@ -51,7 +52,7 @@
   void DisplayDidDrawAndSwap() override {}
 
  private:
-  scoped_refptr<cc::ContextProvider> display_context_provider_;
+  scoped_refptr<viz::ContextProvider> display_context_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(FrameSinkClient);
 };
diff --git a/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index ffb0dc0..110268f 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -28,22 +28,23 @@
 }
 
 bool MessagePumpFuchsia::FileDescriptorWatcher::StopWatchingFileDescriptor() {
-  if (handle_ == MX_HANDLE_INVALID)
+  if (!weak_pump_ || handle_ == MX_HANDLE_INVALID)
     return true;
-  uint64_t this_as_key =
-      static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
-  int result = mx_port_cancel(port_, handle_, this_as_key);
+
+  int result = mx_port_cancel(weak_pump_->port_, handle_, wait_key());
   DLOG_IF(ERROR, result != MX_OK)
       << "mx_port_cancel(handle=" << handle_
       << ") failed: " << mx_status_get_string(result);
   handle_ = MX_HANDLE_INVALID;
+
   return result == MX_OK;
 }
 
-MessagePumpFuchsia::MessagePumpFuchsia() : keep_running_(true) {
+MessagePumpFuchsia::MessagePumpFuchsia()
+    : keep_running_(true), weak_factory_(this) {
   // TODO(wez): Remove MX_PORT_OPT_V2 once the SDK is rolled, or migrate
   // this implementation use ulib/port helpers.
-  CHECK(mx_port_create(MX_PORT_OPT_V2, &port_) == MX_OK);
+  CHECK_EQ(0, mx_port_create(MX_PORT_OPT_V2, &port_));
 }
 
 MessagePumpFuchsia::~MessagePumpFuchsia() {
@@ -61,8 +62,10 @@
   DCHECK_GE(fd, 0);
   DCHECK(controller);
   DCHECK(delegate);
+
+  controller->fd_ = fd;
+  controller->persistent_ = persistent;
   controller->watcher_ = delegate;
-  controller->port_ = port_;
 
   uint32_t events = 0;
   switch (mode) {
@@ -79,7 +82,6 @@
       NOTREACHED() << "unexpected mode: " << mode;
       return false;
   }
-
   controller->desired_events_ = events;
 
   controller->io_ = __mxio_fd_to_io(fd);
@@ -88,8 +90,7 @@
     return false;
   }
 
-  controller->fd_ = fd;
-  controller->persistent_ = persistent;
+  controller->weak_pump_ = weak_factory_.GetWeakPtr();
 
   return controller->WaitBegin();
 }
@@ -102,13 +103,12 @@
     return false;
   }
 
-  uint64_t this_as_key =
-      static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
-  mx_status_t status = mx_object_wait_async(handle_, port_, this_as_key,
-                                            signals, MX_WAIT_ASYNC_ONCE);
+  mx_status_t status = mx_object_wait_async(
+      handle_, weak_pump_->port_, wait_key(), signals, MX_WAIT_ASYNC_ONCE);
   if (status != MX_OK) {
     DLOG(ERROR) << "mx_object_wait_async failed: "
-                << mx_status_get_string(status) << " (port=" << port_ << ")";
+                << mx_status_get_string(status)
+                << " (port=" << weak_pump_->port_ << ")";
     return false;
   }
   return true;
diff --git a/base/message_loop/message_pump_fuchsia.h b/base/message_loop/message_pump_fuchsia.h
index fec4ab5..001de34 100644
--- a/base/message_loop/message_pump_fuchsia.h
+++ b/base/message_loop/message_pump_fuchsia.h
@@ -8,6 +8,7 @@
 #include "base/base_export.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_pump.h"
 
 #include <magenta/syscalls/port.h>
@@ -44,6 +45,8 @@
     }
 
    private:
+    friend class MessagePumpFuchsia;
+
     // Start watching the FD.
     bool WaitBegin();
 
@@ -51,13 +54,15 @@
     // in based on the observed bits from the underlying packet.
     uint32_t WaitEnd(uint32_t observed);
 
-    friend class MessagePumpFuchsia;
+    // Returns the key to use to uniquely identify this object's wait operation.
+    uint64_t wait_key() const {
+      return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
+    }
 
     const tracked_objects::Location created_from_location_;
 
     // Set directly from the inputs to WatchFileDescriptor.
     Watcher* watcher_ = nullptr;
-    mx_handle_t port_ = MX_HANDLE_INVALID;
     int fd_ = -1;
     uint32_t desired_events_ = 0;
 
@@ -68,6 +73,9 @@
     // WaitBegin and WaitEnd calls), and MX_HANDLE_INVALID otherwise.
     mx_handle_t handle_ = MX_HANDLE_INVALID;
 
+    // Used to safely access resources owned by the associated message pump.
+    WeakPtr<MessagePumpFuchsia> weak_pump_;
+
     // This bool is used during calling |Watcher| callbacks. This object's
     // lifetime is owned by the user of this class. If the message loop is woken
     // up in the case where it needs to call both the readable and writable
@@ -113,6 +121,8 @@
   // The time at which we should call DoDelayedWork.
   TimeTicks delayed_work_time_;
 
+  base::WeakPtrFactory<MessagePumpFuchsia> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MessagePumpFuchsia);
 };
 
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index ac6c9aa..1266d826 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -9,6 +9,7 @@
 
 #include "base/metrics/histogram.h"
 
+#include <inttypes.h>
 #include <limits.h>
 #include <math.h>
 
@@ -18,6 +19,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -31,6 +33,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "base/values.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -554,9 +557,16 @@
     bad_fields |= 1 << kFlagsField;
 
   // Abort if a problem is found (except "flags", which could legally be zero).
-  CHECK_EQ(0U, bad_fields & ~(1 << kFlagsField))
-      << histogram_name() << ": " << bad_fields;
-  debug::Alias(&bad_fields);
+  if ((bad_fields & ~(1 << kFlagsField)) != 0) {
+    const std::string debug_string =
+        base::StringPrintf("%s/%" PRIu32, histogram_name().c_str(), bad_fields);
+#if !defined(OS_NACL)
+    // Temporary for https://crbug.com/736675.
+    base::debug::ScopedCrashKey crash_key("bad_histogram", debug_string);
+#endif
+    CHECK(false) << debug_string;
+    debug::Alias(&bad_fields);
+  }
 }
 
 bool Histogram::SerializeInfoImpl(Pickle* pickle) const {
diff --git a/base/numerics/checked_math.h b/base/numerics/checked_math.h
index 1f8ea9d..66a2e5d 100644
--- a/base/numerics/checked_math.h
+++ b/base/numerics/checked_math.h
@@ -129,12 +129,18 @@
   CheckedNumeric& operator^=(const Src rhs);
 
   constexpr CheckedNumeric operator-() const {
-    return CheckedNumeric<T>(
-        NegateWrapper(state_.value()),
-        IsValid() &&
-            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
-             NegateWrapper(state_.value()) !=
-                 std::numeric_limits<T>::lowest()));
+    // The negation of two's complement int min is int min, so we simply
+    // check for that in the constexpr case.
+    // We use an optimized code path for a known run-time variable.
+    return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
+                   std::is_floating_point<T>::value
+               ? CheckedNumeric<T>(
+                     NegateWrapper(state_.value()),
+                     IsValid() && (!std::is_signed<T>::value ||
+                                   std::is_floating_point<T>::value ||
+                                   NegateWrapper(state_.value()) !=
+                                       std::numeric_limits<T>::lowest()))
+               : FastRuntimeNegate();
   }
 
   constexpr CheckedNumeric operator~() const {
@@ -143,11 +149,7 @@
   }
 
   constexpr CheckedNumeric Abs() const {
-    return CheckedNumeric<T>(
-        AbsWrapper(state_.value()),
-        IsValid() &&
-            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
-             AbsWrapper(state_.value()) != std::numeric_limits<T>::lowest()));
+    return !IsValueNegative(state_.value()) ? *this : -*this;
   }
 
   template <typename U>
@@ -239,6 +241,12 @@
  private:
   CheckedNumericState<T> state_;
 
+  CheckedNumeric FastRuntimeNegate() const {
+    T result;
+    bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
+    return CheckedNumeric<T>(result, IsValid() && success);
+  }
+
   template <typename Src>
   constexpr CheckedNumeric(Src value, bool is_valid)
       : state_(value, is_valid) {}
diff --git a/base/numerics/clamped_math.h b/base/numerics/clamped_math.h
index d4ffa3e..0ef1285 100644
--- a/base/numerics/clamped_math.h
+++ b/base/numerics/clamped_math.h
@@ -79,11 +79,11 @@
 
   constexpr ClampedNumeric operator-() const {
     return ClampedNumeric<T>(
-        // The negation of two's complement int min is int min, so that's the
-        // only overflow case we have to check for. And in the case of a
-        // run-time variable value_, we can use an optimized code path.
+        // The negation of two's complement int min is int min, so we can
+        // check and just add one to prevent overflow in the constexpr case.
+        // We use an optimized code path for a known run-time variable.
         std::is_signed<T>::value
-            ? (IsCompileTimeConstant(value_)
+            ? (MustTreatAsConstexpr(value_)
                    ? ((std::is_floating_point<T>::value ||
                        NegateWrapper(value_) !=
                            std::numeric_limits<T>::lowest())
@@ -98,13 +98,9 @@
   }
 
   constexpr ClampedNumeric Abs() const {
-    return ClampedNumeric<T>(
-        // The negation of two's complement int min is int min, so that's the
-        // only overflow case we have to check for.
-        (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
-         AbsWrapper(value_) != std::numeric_limits<T>::lowest())
-            ? AbsWrapper(value_)
-            : std::numeric_limits<T>::max());
+    // The negation of two's complement int min is int min, so that's the
+    // only overflow case where we will saturate.
+    return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
   }
 
   template <typename U>
diff --git a/base/numerics/clamped_math_impl.h b/base/numerics/clamped_math_impl.h
index cbd96cb..a429b29 100644
--- a/base/numerics/clamped_math_impl.h
+++ b/base/numerics/clamped_math_impl.h
@@ -21,6 +21,30 @@
 namespace base {
 namespace internal {
 
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+  // The calculation below is a static identity for unsigned types, but for
+  // signed integer types it provides a non-branching, saturated absolute value.
+  // This works because SafeUnsignedAbs() returns an unsigned type, which can
+  // represent the absolute value of all negative numbers of an equal-width
+  // integer type. The call to IsValueNegative() then detects overflow in the
+  // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
+  // a signed integer value. If it is the overflow case, we end up subtracting
+  // one from the unsigned result, thus saturating to numeric_limits<T>::max().
+  return MustTreatAsConstexpr(value) && !ClampedAbsFastOp<T>::is_supported
+             ? static_cast<T>(SafeUnsignedAbs(value) -
+                              IsValueNegative<T>(SafeUnsignedAbs(value)))
+             : ClampedAbsFastOp<T>::Do(value);
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+  return value < 0 ? -value : value;
+}
+
 template <typename T, typename U, class Enable = void>
 struct ClampedAddOp {};
 
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h
index 46234005..fe03f28 100644
--- a/base/numerics/safe_conversions_impl.h
+++ b/base/numerics/safe_conversions_impl.h
@@ -76,15 +76,29 @@
                                 : static_cast<UnsignedT>(value);
 }
 
-// This provides a small optimization that generates more compact code when one
-// of the components in an operation is a compile-time constant.
+// This allows us to switch paths on known compile-time constants.
+#if defined(__clang__) || defined(__GNUC__)
+constexpr bool CanDetectCompileTimeConstant() {
+  return true;
+}
 template <typename T>
 constexpr bool IsCompileTimeConstant(const T v) {
-#if defined(__clang__) || defined(__GNUC__)
   return __builtin_constant_p(v);
+}
 #else
+constexpr bool CanDetectCompileTimeConstant() {
   return false;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T v) {
+  return false;
+}
 #endif
+template <typename T>
+constexpr bool MustTreatAsConstexpr(const T v) {
+  // Either we can't detect a compile-time constant, and must always use the
+  // constexpr path, or we know we have a compile-time constant.
+  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
 }
 
 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
diff --git a/base/numerics/safe_math_clang_gcc_impl.h b/base/numerics/safe_math_clang_gcc_impl.h
index a08534ba..fbedea4 100644
--- a/base/numerics/safe_math_clang_gcc_impl.h
+++ b/base/numerics/safe_math_clang_gcc_impl.h
@@ -216,6 +216,22 @@
   }
 };
 
+template <typename T>
+struct ClampedAbsFastOp {
+// The generic code is pretty much optimal on arm, so we use it instead.
+#if defined(__ARMEL__) || defined(__arch64__)
+  static const bool is_supported = false;
+#else
+  static const bool is_supported = std::is_signed<T>::value;
+#endif
+  static T Do(T value) {
+    // This variable assignment is necessary to prevent the compiler from
+    // emitting longer, ugly, branchy code.
+    T negated = ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
+    return IsValueNegative(value) ? negated : value;
+  }
+};
+
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/numerics/safe_math_shared_impl.h b/base/numerics/safe_math_shared_impl.h
index 603a7eb..99f230c 100644
--- a/base/numerics/safe_math_shared_impl.h
+++ b/base/numerics/safe_math_shared_impl.h
@@ -94,6 +94,15 @@
     return CheckOnFailure::template HandleFailure<V>();
   }
 };
+
+template <typename T>
+struct ClampedAbsFastOp {
+  static const bool is_supported = false;
+  static constexpr T Do(T) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<T>();
+  }
+};
 #endif  // BASE_HAS_OPTIMIZED_SAFE_MATH
 #undef BASE_HAS_OPTIMIZED_SAFE_MATH
 
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc
index 07353443..26033d1b 100644
--- a/base/task_scheduler/scheduler_worker.cc
+++ b/base/task_scheduler/scheduler_worker.cc
@@ -15,6 +15,7 @@
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
 #elif defined(OS_WIN)
+#include "base/win/com_init_check_hook.h"
 #include "base/win/scoped_com_initializer.h"
 #endif
 
@@ -43,7 +44,10 @@
     // A SchedulerWorker starts out waiting for work.
     outer_->delegate_->WaitForWork(&wake_up_event_);
 
-#if defined(OS_WIN)
+    // When defined(COM_INIT_CHECK_HOOK_ENABLED), ignore
+    // SchedulerBackwardCompatibility::INIT_COM_STA to find incorrect uses of
+    // COM that should be running in a COM STA Task Runner.
+#if defined(OS_WIN) && !defined(COM_INIT_CHECK_HOOK_ENABLED)
     std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
     if (outer_->backward_compatibility_ ==
         SchedulerBackwardCompatibility::INIT_COM_STA) {
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc b/base/task_scheduler/scheduler_worker_unittest.cc
index ab5982fb2..4d22f59 100644
--- a/base/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_unittest.cc
@@ -30,6 +30,8 @@
 
 #if defined(OS_WIN)
 #include <objbase.h>
+
+#include "base/win/com_init_check_hook.h"
 #endif
 
 using testing::_;
@@ -931,7 +933,13 @@
 
   // The call to CoInitializeEx() should have returned S_FALSE to indicate that
   // the COM library was already initialized on the thread.
+  // See SchedulerWorker::Thread::ThreadMain for why we expect two different
+  // results here.
+#if defined(COM_INIT_CHECK_HOOK_ENABLED)
+  EXPECT_EQ(S_OK, delegate_raw->coinitialize_hresult());
+#else
   EXPECT_EQ(S_FALSE, delegate_raw->coinitialize_hresult());
+#endif
 
   worker->JoinForTesting();
 }
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index 5c4156cd..2599c79d 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -38,13 +38,13 @@
       from incremental_install import installer
       installer.Install(device, helper, split_globs=params['splits'],
                         native_libs=params['native_libs'],
-                        permissions=None)
+                        dex_files=params['dex_files'], permissions=None)
     devices_obj.pMap(install_incremental_apk)
-    return
-  # Install the regular apk on devices.
-  def install(device):
-    device.Install(apk_to_install)
-  devices_obj.pMap(install)
+  else:
+    # Install the regular apk on devices.
+    def install(device):
+      device.Install(apk_to_install)
+    devices_obj.pMap(install)
 
 
 def _UninstallApk(install_incremental, devices_obj, apk_package):
@@ -316,6 +316,7 @@
   elif command == 'run':
     _InstallApk(install_incremental, inc_install_script, devices_obj,
                 active_apk)
+    devices_obj.pFinish(None)
     _LaunchUrl(devices_obj, args.args, gn_args.command_line_flags_file,
                args.url, apk_package)
   elif command == 'stop':
@@ -342,6 +343,8 @@
     args = [adb_path, 'logcat']
     os.execv(adb_path, args)
 
+  # Wait for all threads to finish.
+  devices_obj.pFinish(None)
 
   # Save back to the cache.
   if use_cache:
diff --git a/build/android/incremental_install/installer.py b/build/android/incremental_install/installer.py
index a35cc93d1..54abf76 100755
--- a/build/android/incremental_install/installer.py
+++ b/build/android/incremental_install/installer.py
@@ -128,8 +128,8 @@
 
   # Push .so and .dex files to the device (if they have changed).
   def do_push_files():
+    push_native_timer.Start()
     if native_libs:
-      push_native_timer.Start()
       with build_utils.TempDir() as temp_dir:
         device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
         for path in native_libs:
@@ -138,10 +138,10 @@
           shutil.copy(path, os.path.join(temp_dir, os.path.basename(path)))
         device.PushChangedFiles([(temp_dir, device_lib_dir)],
                                 delete_device_stale=True)
-      push_native_timer.Stop(log=False)
+    push_native_timer.Stop(log=False)
 
+    push_dex_timer.Start()
     if dex_files:
-      push_dex_timer.Start()
       # Put all .dex files to be pushed into a temporary directory so that we
       # can use delete_device_stale=True.
       with build_utils.TempDir() as temp_dir:
@@ -155,7 +155,7 @@
             shutil.copy(src_path, os.path.join(temp_dir, dest_name))
         device.PushChangedFiles([(temp_dir, device_dex_dir)],
                                 delete_device_stale=True)
-      push_dex_timer.Stop(log=False)
+    push_dex_timer.Stop(log=False)
 
   def check_selinux():
     # Marshmallow has no filesystem access whatsoever. It might be possible to
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 29f538f..67a898b 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -148,10 +148,6 @@
     "output/compositor_frame.h",
     "output/compositor_frame_metadata.cc",
     "output/compositor_frame_metadata.h",
-    "output/context_cache_controller.cc",
-    "output/context_cache_controller.h",
-    "output/context_provider.cc",
-    "output/context_provider.h",
     "output/copy_output_request.cc",
     "output/copy_output_request.h",
     "output/copy_output_result.cc",
@@ -168,8 +164,6 @@
     "output/gl_renderer.h",
     "output/gl_renderer_draw_cache.cc",
     "output/gl_renderer_draw_cache.h",
-    "output/in_process_context_provider.cc",
-    "output/in_process_context_provider.h",
     "output/latency_info_swap_promise.cc",
     "output/latency_info_swap_promise.h",
     "output/layer_quad.cc",
@@ -838,7 +832,6 @@
     "animation/scroll_offset_animation_curve_unittest.cc",
     "animation/transform_operations_unittest.cc",
     "surfaces/surface_hittest_unittest.cc",
-    "surfaces/surface_manager_ref_unittest.cc",
     "surfaces/surface_unittest.cc",
 
     # Setup.
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index f744ccf..12d9b364 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -161,7 +161,7 @@
 void HeadsUpDisplayLayerImpl::UpdateHudTexture(
     DrawMode draw_mode,
     ResourceProvider* resource_provider,
-    ContextProvider* context_provider) {
+    viz::ContextProvider* context_provider) {
   if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE || !resources_.back()->id())
     return;
 
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index b9f6afcd..42af03f 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -45,7 +45,7 @@
                    AppendQuadsData* append_quads_data) override;
   void UpdateHudTexture(DrawMode draw_mode,
                         ResourceProvider* resource_provider,
-                        ContextProvider* context_provider);
+                        viz::ContextProvider* context_provider);
 
   void ReleaseResources() override;
 
diff --git a/cc/layers/heads_up_display_layer_impl_unittest.cc b/cc/layers/heads_up_display_layer_impl_unittest.cc
index 94d3f13..dab961f 100644
--- a/cc/layers/heads_up_display_layer_impl_unittest.cc
+++ b/cc/layers/heads_up_display_layer_impl_unittest.cc
@@ -18,7 +18,7 @@
 
 void CheckDrawLayer(HeadsUpDisplayLayerImpl* layer,
                     ResourceProvider* resource_provider,
-                    ContextProvider* context_provider,
+                    viz::ContextProvider* context_provider,
                     DrawMode draw_mode) {
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
diff --git a/cc/layers/texture_layer_impl_unittest.cc b/cc/layers/texture_layer_impl_unittest.cc
index 57689da..0dc3a8c 100644
--- a/cc/layers/texture_layer_impl_unittest.cc
+++ b/cc/layers/texture_layer_impl_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <stddef.h>
 
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/test/fake_layer_tree_frame_sink.h"
 #include "cc/test/layer_test_common.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 623ac76..cdbc20e 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -25,7 +25,6 @@
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/layers/texture_layer_impl.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/returned_resource.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_frame_sink.h"
@@ -40,6 +39,7 @@
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/single_thread_proxy.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/test/test_layer_tree_frame_sink.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -639,8 +639,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     constexpr bool disable_display_vsync = false;
     bool synchronous_composite =
         !HasImplThread() &&
diff --git a/cc/layers/video_layer_impl_unittest.cc b/cc/layers/video_layer_impl_unittest.cc
index 8b62a69..7f0adc3d 100644
--- a/cc/layers/video_layer_impl_unittest.cc
+++ b/cc/layers/video_layer_impl_unittest.cc
@@ -7,7 +7,6 @@
 #include <stddef.h>
 
 #include "cc/layers/video_frame_provider_client_impl.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/quads/stream_video_draw_quad.h"
@@ -16,6 +15,7 @@
 #include "cc/test/fake_video_frame_provider.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/trees/single_thread_proxy.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cc/output/context_cache_controller_unittest.cc b/cc/output/context_cache_controller_unittest.cc
index 35e1e25a..5f32ac3c 100644
--- a/cc/output/context_cache_controller_unittest.cc
+++ b/cc/output/context_cache_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/context_cache_controller.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 
 #include "base/memory/ptr_util.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -27,10 +27,10 @@
 TEST(ContextCacheControllerTest, ScopedVisibilityBasic) {
   StrictMock<MockContextSupport> context_support;
   auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
-  ContextCacheController cache_controller(&context_support, task_runner);
+  viz::ContextCacheController cache_controller(&context_support, task_runner);
 
   EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
-  std::unique_ptr<ContextCacheController::ScopedVisibility> visibility =
+  std::unique_ptr<viz::ContextCacheController::ScopedVisibility> visibility =
       cache_controller.ClientBecameVisible();
   Mock::VerifyAndClearExpectations(&context_support);
 
@@ -41,7 +41,7 @@
 TEST(ContextCacheControllerTest, ScopedVisibilityMulti) {
   StrictMock<MockContextSupport> context_support;
   auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
-  ContextCacheController cache_controller(&context_support, task_runner);
+  viz::ContextCacheController cache_controller(&context_support, task_runner);
 
   EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
   auto visibility_1 = cache_controller.ClientBecameVisible();
@@ -56,7 +56,7 @@
 TEST(ContextCacheControllerTest, ScopedBusyWhileVisible) {
   StrictMock<MockContextSupport> context_support;
   auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
-  ContextCacheController cache_controller(&context_support, task_runner);
+  viz::ContextCacheController cache_controller(&context_support, task_runner);
 
   EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
   auto visibility = cache_controller.ClientBecameVisible();
@@ -78,7 +78,7 @@
 TEST(ContextCacheControllerTest, ScopedBusyWhileNotVisible) {
   StrictMock<MockContextSupport> context_support;
   auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
-  ContextCacheController cache_controller(&context_support, task_runner);
+  viz::ContextCacheController cache_controller(&context_support, task_runner);
 
   auto busy = cache_controller.ClientBecameBusy();
 
@@ -90,7 +90,7 @@
 TEST(ContextCacheControllerTest, ScopedBusyMulitpleWhileVisible) {
   StrictMock<MockContextSupport> context_support;
   auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
-  ContextCacheController cache_controller(&context_support, task_runner);
+  viz::ContextCacheController cache_controller(&context_support, task_runner);
 
   EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
   auto visible = cache_controller.ClientBecameVisible();
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 0aaefe88..8d5dfaa 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -31,7 +31,6 @@
 #include "cc/debug/debug_colors.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/output/compositor_frame_metadata.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/output/dynamic_geometry_binding.h"
 #include "cc/output/layer_quad.h"
@@ -48,6 +47,7 @@
 #include "cc/raster/scoped_gpu_raster.h"
 #include "cc/resources/resource_pool.h"
 #include "cc/resources/scoped_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -245,7 +245,7 @@
     // GrContext for filters is created lazily, and may fail if the context
     // is lost.
     // TODO(vmiura,bsalomon): crbug.com/487850 Ensure that
-    // ContextProvider::GrContext() does not return NULL.
+    // viz::ContextProvider::GrContext() does not return NULL.
     if (renderer->output_surface_->context_provider()->GrContext())
       return base::WrapUnique(new ScopedUseGrContext(renderer));
     return nullptr;
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 7bb6556..f4669b2c 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "cc/cc_export.h"
 #include "cc/output/color_lut_cache.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/output/direct_renderer.h"
 #include "cc/output/gl_renderer_draw_cache.h"
 #include "cc/output/program_binding.h"
@@ -22,6 +21,7 @@
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/tile_draw_quad.h"
 #include "cc/quads/yuv_video_draw_quad.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "ui/gfx/geometry/quad_f.h"
 #include "ui/latency/latency_info.h"
 
@@ -309,7 +309,8 @@
 
   gpu::gles2::GLES2Interface* gl_;
   gpu::ContextSupport* context_support_;
-  std::unique_ptr<ContextCacheController::ScopedVisibility> context_visibility_;
+  std::unique_ptr<viz::ContextCacheController::ScopedVisibility>
+      context_visibility_;
 
   TextureMailboxDeleter* texture_mailbox_deleter_;
 
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 88c506a4..c0713fc 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -1610,7 +1610,7 @@
 
 class MockOutputSurface : public OutputSurface {
  public:
-  explicit MockOutputSurface(scoped_refptr<ContextProvider> provider)
+  explicit MockOutputSurface(scoped_refptr<viz::ContextProvider> provider)
       : OutputSurface(std::move(provider)) {}
   virtual ~MockOutputSurface() {}
 
diff --git a/cc/output/layer_tree_frame_sink.cc b/cc/output/layer_tree_frame_sink.cc
index 6ad8324..5de7e2a 100644
--- a/cc/output/layer_tree_frame_sink.cc
+++ b/cc/output/layer_tree_frame_sink.cc
@@ -19,8 +19,8 @@
 namespace cc {
 
 LayerTreeFrameSink::LayerTreeFrameSink(
-    scoped_refptr<ContextProvider> context_provider,
-    scoped_refptr<ContextProvider> worker_context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> worker_context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     viz::SharedBitmapManager* shared_bitmap_manager)
     : context_provider_(std::move(context_provider)),
@@ -55,7 +55,7 @@
   }
 
   if (!success) {
-    // Destroy the ContextProvider on the thread attempted to be bound.
+    // Destroy the viz::ContextProvider on the thread attempted to be bound.
     context_provider_ = nullptr;
     client_ = nullptr;
   }
@@ -68,9 +68,9 @@
 
   if (context_provider_.get()) {
     context_provider_->SetLostContextCallback(
-        ContextProvider::LostContextCallback());
+        viz::ContextProvider::LostContextCallback());
   }
-  // Destroy the ContextProvider on the bound thread.
+  // Destroy the viz::ContextProvider on the bound thread.
   context_provider_ = nullptr;
   client_ = nullptr;
 }
diff --git a/cc/output/layer_tree_frame_sink.h b/cc/output/layer_tree_frame_sink.h
index 514f35ee..eded4db7 100644
--- a/cc/output/layer_tree_frame_sink.h
+++ b/cc/output/layer_tree_frame_sink.h
@@ -12,10 +12,10 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/overlay_candidate_validator.h"
 #include "cc/output/vulkan_context_provider.h"
 #include "cc/resources/returned_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
 #include "ui/gfx/color_space.h"
 
@@ -64,10 +64,11 @@
   // present.
   // gpu_memory_buffer_manager is optional (won't be used) if context_provider
   // is not present.
-  LayerTreeFrameSink(scoped_refptr<ContextProvider> context_provider,
-                     scoped_refptr<ContextProvider> worker_context_provider,
-                     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-                     viz::SharedBitmapManager* shared_bitmap_manager);
+  LayerTreeFrameSink(
+      scoped_refptr<viz::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider,
+      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      viz::SharedBitmapManager* shared_bitmap_manager);
 
   // Constructor for Vulkan-based resources.
   explicit LayerTreeFrameSink(
@@ -94,10 +95,12 @@
 
   const Capabilities& capabilities() const { return capabilities_; }
 
-  // The ContextProviders may be null if frames should be submitted with
+  // The viz::ContextProviders may be null if frames should be submitted with
   // software SharedBitmap resources.
-  ContextProvider* context_provider() const { return context_provider_.get(); }
-  ContextProvider* worker_context_provider() const {
+  viz::ContextProvider* context_provider() const {
+    return context_provider_.get();
+  }
+  viz::ContextProvider* worker_context_provider() const {
     return worker_context_provider_.get();
   }
   VulkanContextProvider* vulkan_context_provider() const {
@@ -129,15 +132,15 @@
   virtual void DidNotProduceFrame(const BeginFrameAck& ack) = 0;
 
  protected:
-  // Bound to the ContextProvider to hear about when it is lost and inform the
-  // |client_|.
+  // Bound to the viz::ContextProvider to hear about when it is lost and inform
+  // the |client_|.
   void DidLoseLayerTreeFrameSink();
 
   LayerTreeFrameSinkClient* client_ = nullptr;
 
   struct LayerTreeFrameSink::Capabilities capabilities_;
-  scoped_refptr<ContextProvider> context_provider_;
-  scoped_refptr<ContextProvider> worker_context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
+  scoped_refptr<viz::ContextProvider> worker_context_provider_;
   scoped_refptr<VulkanContextProvider> vulkan_context_provider_;
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
   viz::SharedBitmapManager* shared_bitmap_manager_;
diff --git a/cc/output/layer_tree_frame_sink_client.h b/cc/output/layer_tree_frame_sink_client.h
index 1015f5b..ef8a96e8 100644
--- a/cc/output/layer_tree_frame_sink_client.h
+++ b/cc/output/layer_tree_frame_sink_client.h
@@ -9,8 +9,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/returned_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -46,9 +46,9 @@
   // so that frames are submitted only at the rate it can handle them.
   virtual void DidReceiveCompositorFrameAck() = 0;
 
-  // The LayerTreeFrameSink is lost when the ContextProviders held by it
+  // The LayerTreeFrameSink is lost when the viz::ContextProviders held by it
   // encounter an error. In this case the LayerTreeFrameSink (and the
-  // ContextProviders) must be recreated.
+  // viz::ContextProviders) must be recreated.
   virtual void DidLoseLayerTreeFrameSink() = 0;
 
   // For SynchronousCompositor (WebView) to ask the layer compositor to submit
diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc
index 9c86c08..6b9b006c 100644
--- a/cc/output/output_surface.cc
+++ b/cc/output/output_surface.cc
@@ -19,7 +19,8 @@
 
 namespace cc {
 
-OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider)
+OutputSurface::OutputSurface(
+    scoped_refptr<viz::ContextProvider> context_provider)
     : context_provider_(std::move(context_provider)) {
   DCHECK(context_provider_);
 }
diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h
index d1715cb..740613d 100644
--- a/cc/output/output_surface.h
+++ b/cc/output/output_surface.h
@@ -12,11 +12,11 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/overlay_candidate_validator.h"
 #include "cc/output/software_output_device.h"
 #include "cc/output/vulkan_context_provider.h"
 #include "cc/resources/returned_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
 #include "ui/gfx/color_space.h"
 
@@ -51,7 +51,7 @@
   };
 
   // Constructor for GL-based compositing.
-  explicit OutputSurface(scoped_refptr<ContextProvider> context_provider);
+  explicit OutputSurface(scoped_refptr<viz::ContextProvider> context_provider);
   // Constructor for software compositing.
   explicit OutputSurface(std::unique_ptr<SoftwareOutputDevice> software_device);
   // Constructor for Vulkan-based compositing.
@@ -66,7 +66,9 @@
   // surface. Either of these may return a null pointer, but not both.
   // In the event of a lost context, the entire output surface should be
   // recreated.
-  ContextProvider* context_provider() const { return context_provider_.get(); }
+  viz::ContextProvider* context_provider() const {
+    return context_provider_.get();
+  }
   VulkanContextProvider* vulkan_context_provider() const {
     return vulkan_context_provider_.get();
   }
@@ -120,7 +122,7 @@
 
  protected:
   struct OutputSurface::Capabilities capabilities_;
-  scoped_refptr<ContextProvider> context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
   scoped_refptr<VulkanContextProvider> vulkan_context_provider_;
   std::unique_ptr<SoftwareOutputDevice> software_device_;
 
diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h
index c3f3df4..43acf529 100644
--- a/cc/output/output_surface_client.h
+++ b/cc/output/output_surface_client.h
@@ -9,8 +9,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/returned_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
 #include "ui/gfx/geometry/rect.h"
 
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
index 6f1bc91..03e548d1 100644
--- a/cc/output/program_binding.h
+++ b/cc/output/program_binding.h
@@ -9,8 +9,8 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/shader.h"
+#include "components/viz/common/gpu/context_provider.h"
 
 namespace gfx {
 class ColorTransform;
@@ -155,7 +155,8 @@
  public:
   Program() {}
 
-  void Initialize(ContextProvider* context_provider, const ProgramKey& key) {
+  void Initialize(viz::ContextProvider* context_provider,
+                  const ProgramKey& key) {
     // Set parameters that are common to all sub-classes.
     vertex_shader_.aa_mode_ = key.aa_mode_;
     fragment_shader_.aa_mode_ = key.aa_mode_;
@@ -396,7 +397,7 @@
     fragment_shader_.uv_texture_mode_ = key.uv_texture_mode_;
   }
 
-  void InitializeInternal(ContextProvider* context_provider) {
+  void InitializeInternal(viz::ContextProvider* context_provider) {
     DCHECK(context_provider);
     DCHECK(!initialized_);
 
diff --git a/cc/output/texture_mailbox_deleter.cc b/cc/output/texture_mailbox_deleter.cc
index 1e306a7..b72642e 100644
--- a/cc/output/texture_mailbox_deleter.cc
+++ b/cc/output/texture_mailbox_deleter.cc
@@ -10,15 +10,15 @@
 #include "base/location.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/single_release_callback.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/sync_token.h"
 
 namespace cc {
 
 static void DeleteTextureOnImplThread(
-    const scoped_refptr<ContextProvider>& context_provider,
+    const scoped_refptr<viz::ContextProvider>& context_provider,
     unsigned texture_id,
     const gpu::SyncToken& sync_token,
     bool is_lost) {
@@ -50,7 +50,7 @@
 
 std::unique_ptr<SingleReleaseCallback>
 TextureMailboxDeleter::GetReleaseCallback(
-    scoped_refptr<ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     unsigned texture_id) {
   // This callback owns the |context_provider|. It must be destroyed on the impl
   // thread. Upon destruction of this class, the callback must immediately be
diff --git a/cc/output/texture_mailbox_deleter.h b/cc/output/texture_mailbox_deleter.h
index 8003583..326c494 100644
--- a/cc/output/texture_mailbox_deleter.h
+++ b/cc/output/texture_mailbox_deleter.h
@@ -19,8 +19,11 @@
 struct SyncToken;
 }
 
-namespace cc {
+namespace viz {
 class ContextProvider;
+}
+
+namespace cc {
 class SingleReleaseCallback;
 
 class CC_EXPORT TextureMailboxDeleter {
@@ -37,9 +40,9 @@
   // run, on the impl thread. If the TextureMailboxDeleter is destroyed
   // due to the compositor shutting down, then the ReleaseCallback will
   // become a no-op and the texture will be deleted immediately on the
-  // impl thread, along with dropping the reference to the ContextProvider.
+  // impl thread, along with dropping the reference to the viz::ContextProvider.
   std::unique_ptr<SingleReleaseCallback> GetReleaseCallback(
-      scoped_refptr<ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
       unsigned texture_id);
 
  private:
diff --git a/cc/output/texture_mailbox_deleter_unittest.cc b/cc/output/texture_mailbox_deleter_unittest.cc
index 205aede2..37c300d0 100644
--- a/cc/output/texture_mailbox_deleter_unittest.cc
+++ b/cc/output/texture_mailbox_deleter_unittest.cc
@@ -34,7 +34,7 @@
   EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
 
   // When the deleter is destroyed, it immediately drops its ref on the
-  // ContextProvider, and deletes the texture.
+  // viz::ContextProvider, and deletes the texture.
   deleter = nullptr;
   EXPECT_TRUE(context_provider->HasOneRef());
   EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
@@ -66,7 +66,7 @@
   cb->Run(gpu::SyncToken(), false);
 
   // With no task runner the callback will immediately drops its ref on the
-  // ContextProvider and delete the texture.
+  // viz::ContextProvider and delete the texture.
   EXPECT_TRUE(context_provider->HasOneRef());
   EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
 }
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 6cc9499..4edfc294 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -36,7 +36,7 @@
     const gfx::Rect& raster_dirty_rect,
     const gfx::AxisTransform2d& transform,
     const RasterSource::PlaybackSettings& playback_settings,
-    ContextProvider* context_provider,
+    viz::ContextProvider* context_provider,
     ResourceProvider::ScopedWriteLockGL* resource_lock,
     bool async_worker_context_enabled,
     bool use_distance_field_text,
@@ -115,8 +115,8 @@
 }
 
 GpuRasterBufferProvider::GpuRasterBufferProvider(
-    ContextProvider* compositor_context_provider,
-    ContextProvider* worker_context_provider,
+    viz::ContextProvider* compositor_context_provider,
+    viz::ContextProvider* worker_context_provider,
     ResourceProvider* resource_provider,
     bool use_distance_field_text,
     int gpu_rasterization_msaa_sample_count,
@@ -264,7 +264,8 @@
     uint64_t new_content_id,
     const gfx::AxisTransform2d& transform,
     const RasterSource::PlaybackSettings& playback_settings) {
-  ContextProvider::ScopedContextLock scoped_context(worker_context_provider_);
+  viz::ContextProvider::ScopedContextLock scoped_context(
+      worker_context_provider_);
   gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
   DCHECK(gl);
 
diff --git a/cc/raster/gpu_raster_buffer_provider.h b/cc/raster/gpu_raster_buffer_provider.h
index 7c194d2..7910541 100644
--- a/cc/raster/gpu_raster_buffer_provider.h
+++ b/cc/raster/gpu_raster_buffer_provider.h
@@ -12,13 +12,16 @@
 #include "cc/resources/resource_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
 
-namespace cc {
+namespace viz {
 class ContextProvider;
+}
+
+namespace cc {
 
 class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider {
  public:
-  GpuRasterBufferProvider(ContextProvider* compositor_context_provider,
-                          ContextProvider* worker_context_provider,
+  GpuRasterBufferProvider(viz::ContextProvider* compositor_context_provider,
+                          viz::ContextProvider* worker_context_provider,
                           ResourceProvider* resource_provider,
                           bool use_distance_field_text,
                           int gpu_rasterization_msaa_sample_count,
@@ -88,8 +91,8 @@
     DISALLOW_COPY_AND_ASSIGN(RasterBufferImpl);
   };
 
-  ContextProvider* const compositor_context_provider_;
-  ContextProvider* const worker_context_provider_;
+  viz::ContextProvider* const compositor_context_provider_;
+  viz::ContextProvider* const worker_context_provider_;
   ResourceProvider* const resource_provider_;
   const bool use_distance_field_text_;
   const int msaa_sample_count_;
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index 2ff956c..8bda2a0 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -68,8 +68,8 @@
 
 OneCopyRasterBufferProvider::OneCopyRasterBufferProvider(
     base::SequencedTaskRunner* task_runner,
-    ContextProvider* compositor_context_provider,
-    ContextProvider* worker_context_provider,
+    viz::ContextProvider* compositor_context_provider,
+    viz::ContextProvider* worker_context_provider,
     ResourceProvider* resource_provider,
     int max_copy_texture_chromium_size,
     bool use_partial_raster,
@@ -234,7 +234,8 @@
     // context was lost before ScheduleTasks was called.
     if (!sync_token.HasData())
       return;
-    ContextProvider::ScopedContextLock scoped_context(worker_context_provider_);
+    viz::ContextProvider::ScopedContextLock scoped_context(
+        worker_context_provider_);
     gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
     DCHECK(gl);
     // Synchronize with compositor.
@@ -323,7 +324,8 @@
     const gpu::SyncToken& sync_token,
     const RasterSource* raster_source,
     const gfx::Rect& rect_to_copy) {
-  ContextProvider::ScopedContextLock scoped_context(worker_context_provider_);
+  viz::ContextProvider::ScopedContextLock scoped_context(
+      worker_context_provider_);
   gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
   DCHECK(gl);
 
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index 0ed3bf2..aa46670 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -8,10 +8,10 @@
 #include <stdint.h>
 
 #include "base/macros.h"
-#include "cc/output/context_provider.h"
 #include "cc/raster/raster_buffer_provider.h"
 #include "cc/raster/staging_buffer_pool.h"
 #include "cc/resources/resource_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
 
 namespace cc {
@@ -21,8 +21,8 @@
 class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider {
  public:
   OneCopyRasterBufferProvider(base::SequencedTaskRunner* task_runner,
-                              ContextProvider* compositor_context_provider,
-                              ContextProvider* worker_context_provider,
+                              viz::ContextProvider* compositor_context_provider,
+                              viz::ContextProvider* worker_context_provider,
                               ResourceProvider* resource_provider,
                               int max_copy_texture_chromium_size,
                               bool use_partial_raster,
@@ -114,8 +114,8 @@
                           const gfx::Rect& rect_to_copy);
   gfx::BufferUsage StagingBufferUsage() const;
 
-  ContextProvider* const compositor_context_provider_;
-  ContextProvider* const worker_context_provider_;
+  viz::ContextProvider* const compositor_context_provider_;
+  viz::ContextProvider* const worker_context_provider_;
   ResourceProvider* const resource_provider_;
   const int max_bytes_per_copy_operation_;
   const bool use_partial_raster_;
diff --git a/cc/raster/raster_buffer_provider.cc b/cc/raster/raster_buffer_provider.cc
index f669b643..1eb0fd03 100644
--- a/cc/raster/raster_buffer_provider.cc
+++ b/cc/raster/raster_buffer_provider.cc
@@ -24,58 +24,6 @@
 
 namespace {
 
-// TODO(enne): http://crbug.com/721744.  Add CHECKs for conditions that would
-// cause Skia to not create a surface here to diagnose what's going wrong.  This
-// replicates SkSurfaceValidateRasterInfo and needs to be kept in sync with
-// the corresponding Skia code.  This code should be removed as quickly as
-// possible once a diagnosis is made.
-void CheckValidRasterInfo(const SkImageInfo& info,
-                          void* pixels,
-                          size_t row_bytes) {
-  CHECK(pixels);
-  CHECK(!info.isEmpty());
-
-  static const size_t kMaxTotalSize = SK_MaxS32;
-
-  int shift = 0;
-  switch (info.colorType()) {
-    case kAlpha_8_SkColorType:
-      CHECK(!info.colorSpace());
-      shift = 0;
-      break;
-    case kRGB_565_SkColorType:
-      CHECK(!info.colorSpace());
-      shift = 1;
-      break;
-    case kN32_SkColorType:
-      if (info.colorSpace())
-        CHECK(info.colorSpace()->gammaCloseToSRGB());
-      shift = 2;
-      break;
-    case kRGBA_F16_SkColorType:
-      if (info.colorSpace())
-        CHECK(info.colorSpace()->gammaIsLinear());
-      shift = 3;
-      break;
-    default:
-      CHECK(false) << "Unknown color type";
-      break;
-  }
-
-  static constexpr size_t kIgnoreRowBytesValue = static_cast<size_t>(~0);
-  if (kIgnoreRowBytesValue == row_bytes)
-    return;
-
-  uint64_t min_row_bytes = static_cast<uint64_t>(info.width()) << shift;
-  CHECK_LE(min_row_bytes, row_bytes);
-
-  size_t aligned_row_bytes = row_bytes >> shift << shift;
-  CHECK_EQ(aligned_row_bytes, row_bytes);
-
-  uint64_t size = sk_64_mul(info.height(), row_bytes);
-  CHECK_LE(size, kMaxTotalSize);
-}
-
 bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) {
   switch (format) {
     case viz::RGBA_4444:
@@ -133,9 +81,12 @@
     case viz::RGBA_8888:
     case viz::BGRA_8888:
     case viz::RGBA_F16: {
-      CheckValidRasterInfo(info, memory, stride);
       sk_sp<SkSurface> surface =
           SkSurface::MakeRasterDirect(info, memory, stride, &surface_props);
+      // There are some rare crashes where this doesn't succeed and may be
+      // indicative of memory stomps elsewhere.  Instead of displaying
+      // invalid content, just crash the renderer and try again.
+      // See: http://crbug.com/721744.
       CHECK(surface);
       raster_source->PlaybackToCanvas(surface->getCanvas(), target_color_space,
                                       canvas_bitmap_rect, canvas_playback_rect,
diff --git a/cc/raster/raster_buffer_provider_perftest.cc b/cc/raster/raster_buffer_provider_perftest.cc
index 7a6fc82..7056b94 100644
--- a/cc/raster/raster_buffer_provider_perftest.cc
+++ b/cc/raster/raster_buffer_provider_perftest.cc
@@ -10,8 +10,6 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "cc/base/lap_timer.h"
-#include "cc/output/context_cache_controller.h"
-#include "cc/output/context_provider.h"
 #include "cc/raster/bitmap_raster_buffer_provider.h"
 #include "cc/raster/gpu_raster_buffer_provider.h"
 #include "cc/raster/one_copy_raster_buffer_provider.h"
@@ -28,6 +26,8 @@
 #include "cc/test/test_shared_bitmap_manager.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/tiles/tile_task_manager.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/platform_color.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -78,7 +78,7 @@
   }
 };
 
-class PerfContextProvider : public ContextProvider {
+class PerfContextProvider : public viz::ContextProvider {
  public:
   PerfContextProvider()
       : context_gl_(new PerfGLES2Interface),
@@ -103,7 +103,7 @@
     cache_controller_.SetGrContext(gr_context_.get());
     return gr_context_.get();
   }
-  ContextCacheController* CacheController() override {
+  viz::ContextCacheController* CacheController() override {
     return &cache_controller_;
   }
   void InvalidateGrContext(uint32_t state) override {
@@ -119,7 +119,7 @@
   std::unique_ptr<PerfGLES2Interface> context_gl_;
   sk_sp<class GrContext> gr_context_;
   TestContextSupport support_;
-  ContextCacheController cache_controller_;
+  viz::ContextCacheController cache_controller_;
   base::Lock context_lock_;
 };
 
@@ -307,8 +307,8 @@
   }
 
  protected:
-  scoped_refptr<ContextProvider> compositor_context_provider_;
-  scoped_refptr<ContextProvider> worker_context_provider_;
+  scoped_refptr<viz::ContextProvider> compositor_context_provider_;
+  scoped_refptr<viz::ContextProvider> worker_context_provider_;
   std::unique_ptr<ResourceProvider> resource_provider_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   std::unique_ptr<SynchronousTaskGraphRunner> task_graph_runner_;
diff --git a/cc/raster/raster_buffer_provider_unittest.cc b/cc/raster/raster_buffer_provider_unittest.cc
index 737016dd..09102fd 100644
--- a/cc/raster/raster_buffer_provider_unittest.cc
+++ b/cc/raster/raster_buffer_provider_unittest.cc
@@ -275,7 +275,7 @@
     return completed_tasks_;
   }
 
-  void LoseContext(ContextProvider* context_provider) {
+  void LoseContext(viz::ContextProvider* context_provider) {
     if (!context_provider)
       return;
     context_provider->ContextGL()->LoseContextCHROMIUM(
diff --git a/cc/raster/scoped_gpu_raster.cc b/cc/raster/scoped_gpu_raster.cc
index 9807c29..c388007 100644
--- a/cc/raster/scoped_gpu_raster.cc
+++ b/cc/raster/scoped_gpu_raster.cc
@@ -12,7 +12,7 @@
 
 namespace cc {
 
-ScopedGpuRaster::ScopedGpuRaster(ContextProvider* context_provider)
+ScopedGpuRaster::ScopedGpuRaster(viz::ContextProvider* context_provider)
     : context_provider_(context_provider) {
   BeginGpuRaster();
 }
diff --git a/cc/raster/scoped_gpu_raster.h b/cc/raster/scoped_gpu_raster.h
index dea7f275..90aca8d 100644
--- a/cc/raster/scoped_gpu_raster.h
+++ b/cc/raster/scoped_gpu_raster.h
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 
 namespace cc {
 
@@ -19,14 +19,14 @@
 // GL resources while an instance of this class is alive.
 class CC_EXPORT ScopedGpuRaster {
  public:
-  explicit ScopedGpuRaster(ContextProvider* context_provider);
+  explicit ScopedGpuRaster(viz::ContextProvider* context_provider);
   ~ScopedGpuRaster();
 
  private:
   void BeginGpuRaster();
   void EndGpuRaster();
 
-  ContextProvider* context_provider_;
+  viz::ContextProvider* context_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedGpuRaster);
 };
diff --git a/cc/raster/staging_buffer_pool.cc b/cc/raster/staging_buffer_pool.cc
index 69cd406..bcfa1c5 100644
--- a/cc/raster/staging_buffer_pool.cc
+++ b/cc/raster/staging_buffer_pool.cc
@@ -128,11 +128,12 @@
   }
 }
 
-StagingBufferPool::StagingBufferPool(base::SequencedTaskRunner* task_runner,
-                                     ContextProvider* worker_context_provider,
-                                     ResourceProvider* resource_provider,
-                                     bool use_partial_raster,
-                                     int max_staging_buffer_usage_in_bytes)
+StagingBufferPool::StagingBufferPool(
+    base::SequencedTaskRunner* task_runner,
+    viz::ContextProvider* worker_context_provider,
+    ResourceProvider* resource_provider,
+    bool use_partial_raster,
+    int max_staging_buffer_usage_in_bytes)
     : task_runner_(task_runner),
       worker_context_provider_(worker_context_provider),
       resource_provider_(resource_provider),
@@ -263,7 +264,8 @@
 
   std::unique_ptr<StagingBuffer> staging_buffer;
 
-  ContextProvider::ScopedContextLock scoped_context(worker_context_provider_);
+  viz::ContextProvider::ScopedContextLock scoped_context(
+      worker_context_provider_);
 
   gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
   DCHECK(gl);
@@ -409,7 +411,8 @@
   lock_.AssertAcquired();
 
   {
-    ContextProvider::ScopedContextLock scoped_context(worker_context_provider_);
+    viz::ContextProvider::ScopedContextLock scoped_context(
+        worker_context_provider_);
 
     gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
     DCHECK(gl);
diff --git a/cc/raster/staging_buffer_pool.h b/cc/raster/staging_buffer_pool.h
index 0591cca..3ab66f9 100644
--- a/cc/raster/staging_buffer_pool.h
+++ b/cc/raster/staging_buffer_pool.h
@@ -18,8 +18,8 @@
 #include "base/time/time.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/trace_event.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/resource_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 
 namespace gpu {
 namespace gles2 {
@@ -56,7 +56,7 @@
   ~StagingBufferPool() final;
 
   StagingBufferPool(base::SequencedTaskRunner* task_runner,
-                    ContextProvider* worker_context_provider,
+                    viz::ContextProvider* worker_context_provider,
                     ResourceProvider* resource_provider,
                     bool use_partial_raster,
                     int max_staging_buffer_usage_in_bytes);
@@ -93,7 +93,7 @@
   void OnPurgeMemory() override;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  ContextProvider* const worker_context_provider_;
+  viz::ContextProvider* const worker_context_provider_;
   ResourceProvider* const resource_provider_;
   const bool use_partial_raster_;
 
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 6c0383e..cbd33ff4 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -368,7 +368,7 @@
 ResourceProvider::Child::~Child() {}
 
 ResourceProvider::Settings::Settings(
-    ContextProvider* compositor_context_provider,
+    viz::ContextProvider* compositor_context_provider,
     bool delegated_sync_points_required,
     bool enable_color_correct_rasterization,
     const viz::ResourceSettings& resource_settings)
@@ -412,7 +412,7 @@
 }
 
 ResourceProvider::ResourceProvider(
-    ContextProvider* compositor_context_provider,
+    viz::ContextProvider* compositor_context_provider,
     viz::SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     BlockingTaskRunner* blocking_main_thread_task_runner,
@@ -1170,7 +1170,7 @@
 }
 
 ResourceProvider::ScopedSkSurfaceProvider::ScopedSkSurfaceProvider(
-    ContextProvider* context_provider,
+    viz::ContextProvider* context_provider,
     ScopedWriteLockGL* resource_lock,
     bool use_mailbox,
     bool use_distance_field_text,
@@ -2053,7 +2053,7 @@
 }
 
 GLES2Interface* ResourceProvider::ContextGL() const {
-  ContextProvider* context_provider = compositor_context_provider_;
+  viz::ContextProvider* context_provider = compositor_context_provider_;
   return context_provider ? context_provider->ContextGL() : nullptr;
 }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index e9e6147..c4d3873d 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -26,13 +26,13 @@
 #include "base/trace_event/memory_dump_provider.h"
 #include "cc/base/resource_id.h"
 #include "cc/cc_export.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface.h"
 #include "cc/output/renderer_settings.h"
 #include "cc/resources/release_callback_impl.h"
 #include "cc/resources/return_callback.h"
 #include "cc/resources/single_release_callback_impl.h"
 #include "cc/resources/transferable_resource.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/quads/resource_format.h"
 #include "components/viz/common/quads/shared_bitmap.h"
 #include "components/viz/common/quads/texture_mailbox.h"
@@ -84,7 +84,7 @@
     RESOURCE_TYPE_BITMAP,
   };
 
-  ResourceProvider(ContextProvider* compositor_context_provider,
+  ResourceProvider(viz::ContextProvider* compositor_context_provider,
                    viz::SharedBitmapManager* shared_bitmap_manager,
                    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
                    BlockingTaskRunner* blocking_main_thread_task_runner,
@@ -95,7 +95,7 @@
 
   void Initialize();
 
-  void DidLoseContextProvider() { lost_context_provider_ = true; }
+  void DidLoseVulkanContextProvider() { lost_context_provider_ = true; }
 
   int max_texture_size() const { return settings_.max_texture_size; }
   viz::ResourceFormat best_texture_format() const {
@@ -343,7 +343,7 @@
 
   class CC_EXPORT ScopedSkSurfaceProvider {
    public:
-    ScopedSkSurfaceProvider(ContextProvider* context_provider,
+    ScopedSkSurfaceProvider(viz::ContextProvider* context_provider,
                             ScopedWriteLockGL* resource_lock,
                             bool use_mailbox,
                             bool use_distance_field_text,
@@ -753,7 +753,7 @@
   // texture target used. The resource must be locked for reading.
   GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter);
 
-  // Returns null if we do not have a ContextProvider.
+  // Returns null if we do not have a viz::ContextProvider.
   gpu::gles2::GLES2Interface* ContextGL() const;
   bool IsGLContextLost() const;
 
@@ -766,7 +766,7 @@
 
   // Holds const settings for the ResourceProvider. Never changed after init.
   struct Settings {
-    Settings(ContextProvider* compositor_context_provider,
+    Settings(viz::ContextProvider* compositor_context_provider,
              bool delegated_sync_points_needed,
              bool enable_color_correct_rasterization,
              const viz::ResourceSettings& resource_settings);
@@ -785,7 +785,7 @@
     bool delegated_sync_points_required = false;
   } const settings_;
 
-  ContextProvider* compositor_context_provider_;
+  viz::ContextProvider* compositor_context_provider_;
   viz::SharedBitmapManager* shared_bitmap_manager_;
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
   BlockingTaskRunner* blocking_main_thread_task_runner_;
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 754fe2da..24cfa916e 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -1391,7 +1391,7 @@
   EXPECT_EQ(0u, returned_to_child.size());
 
   EXPECT_EQ(2u, resource_provider_->num_resources());
-  resource_provider_->DidLoseContextProvider();
+  resource_provider_->DidLoseVulkanContextProvider();
   resource_provider_ = nullptr;
 
   EXPECT_EQ(2u, returned_to_child.size());
@@ -2435,7 +2435,7 @@
   }
 
   // Lose the output surface in the parent.
-  resource_provider_->DidLoseContextProvider();
+  resource_provider_->DidLoseVulkanContextProvider();
 
   {
     EXPECT_EQ(0u, returned_to_child.size());
@@ -2566,7 +2566,7 @@
   }
 
   // Lose the output surface in the parent.
-  resource_provider_->DidLoseContextProvider();
+  resource_provider_->DidLoseVulkanContextProvider();
 
   {
     EXPECT_EQ(0u, returned_to_child.size());
@@ -2734,7 +2734,7 @@
   EXPECT_FALSE(lost_resource);
   EXPECT_FALSE(main_thread_task_runner);
 
-  resource_provider_->DidLoseContextProvider();
+  resource_provider_->DidLoseVulkanContextProvider();
   resource_provider_ = nullptr;
 
   EXPECT_LE(sync_token.release_count(), release_sync_token.release_count());
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 7ec8510..7dbfbb86 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -180,9 +180,10 @@
 
 VideoFrameExternalResources::~VideoFrameExternalResources() {}
 
-VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
-                                           ResourceProvider* resource_provider,
-                                           bool use_stream_video_draw_quad)
+VideoResourceUpdater::VideoResourceUpdater(
+    viz::ContextProvider* context_provider,
+    ResourceProvider* resource_provider,
+    bool use_stream_video_draw_quad)
     : context_provider_(context_provider),
       resource_provider_(resource_provider),
       use_stream_video_draw_quad_(use_stream_video_draw_quad),
@@ -712,7 +713,7 @@
   if (resource_it == updater->all_resources_.end())
     return;
 
-  ContextProvider* context_provider = updater->context_provider_;
+  viz::ContextProvider* context_provider = updater->context_provider_;
   if (context_provider && sync_token.HasData()) {
     context_provider->ContextGL()->WaitSyncTokenCHROMIUM(
         sync_token.GetConstData());
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index 958d016..63b5388a 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -27,8 +27,11 @@
 class VideoFrame;
 }
 
-namespace cc {
+namespace viz {
 class ContextProvider;
+}
+
+namespace cc {
 class ResourceProvider;
 
 class CC_EXPORT VideoFrameExternalResources {
@@ -72,7 +75,7 @@
 // resources consumable by the compositor.
 class CC_EXPORT VideoResourceUpdater {
  public:
-  VideoResourceUpdater(ContextProvider* context_provider,
+  VideoResourceUpdater(viz::ContextProvider* context_provider,
                        ResourceProvider* resource_provider,
                        bool use_stream_video_draw_quad);
   ~VideoResourceUpdater();
@@ -173,7 +176,7 @@
                             bool lost_resource,
                             BlockingTaskRunner* main_thread_task_runner);
 
-  ContextProvider* context_provider_;
+  viz::ContextProvider* context_provider_;
   ResourceProvider* resource_provider_;
   const bool use_stream_video_draw_quad_;
   std::unique_ptr<media::SkCanvasVideoRenderer> video_renderer_;
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h
index 47a1553d..b6573cf 100644
--- a/cc/surfaces/surface_manager.h
+++ b/cc/surfaces/surface_manager.h
@@ -35,6 +35,7 @@
 
 namespace viz {
 namespace test {
+class SurfaceReferencesTest;
 class SurfaceSynchronizationTest;
 }
 }  // namespace viz
@@ -187,7 +188,7 @@
 
  private:
   friend class viz::test::SurfaceSynchronizationTest;
-  friend class SurfaceManagerRefTest;
+  friend class viz::test::SurfaceReferencesTest;
 
   using SurfaceIdSet = std::unordered_set<viz::SurfaceId, viz::SurfaceIdHash>;
 
diff --git a/cc/test/fake_layer_tree_frame_sink.cc b/cc/test/fake_layer_tree_frame_sink.cc
index 0eeca9b5..eb7c306b 100644
--- a/cc/test/fake_layer_tree_frame_sink.cc
+++ b/cc/test/fake_layer_tree_frame_sink.cc
@@ -16,8 +16,8 @@
 namespace cc {
 
 FakeLayerTreeFrameSink::FakeLayerTreeFrameSink(
-    scoped_refptr<ContextProvider> context_provider,
-    scoped_refptr<ContextProvider> worker_context_provider)
+    scoped_refptr<viz::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> worker_context_provider)
     : LayerTreeFrameSink(std::move(context_provider),
                          std::move(worker_context_provider),
                          nullptr,
diff --git a/cc/test/fake_layer_tree_frame_sink.h b/cc/test/fake_layer_tree_frame_sink.h
index 4efc404..a30bf53c 100644
--- a/cc/test/fake_layer_tree_frame_sink.h
+++ b/cc/test/fake_layer_tree_frame_sink.h
@@ -81,8 +81,8 @@
 
  protected:
   FakeLayerTreeFrameSink(
-      scoped_refptr<ContextProvider> context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider);
+      scoped_refptr<viz::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider);
 
   TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
   TestSharedBitmapManager test_shared_bitmap_manager_;
diff --git a/cc/test/fake_output_surface.cc b/cc/test/fake_output_surface.cc
index ac7d345..e809100 100644
--- a/cc/test/fake_output_surface.cc
+++ b/cc/test/fake_output_surface.cc
@@ -14,7 +14,7 @@
 namespace cc {
 
 FakeOutputSurface::FakeOutputSurface(
-    scoped_refptr<ContextProvider> context_provider)
+    scoped_refptr<viz::ContextProvider> context_provider)
     : OutputSurface(std::move(context_provider)), weak_ptr_factory_(this) {
   DCHECK(OutputSurface::context_provider());
 }
diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h
index 05c19013..a80fb38 100644
--- a/cc/test/fake_output_surface.h
+++ b/cc/test/fake_output_surface.h
@@ -32,7 +32,7 @@
   }
 
   static std::unique_ptr<FakeOutputSurface> Create3d(
-      scoped_refptr<ContextProvider> context_provider) {
+      scoped_refptr<viz::ContextProvider> context_provider) {
     return base::WrapUnique(new FakeOutputSurface(context_provider));
   }
 
@@ -42,7 +42,7 @@
   }
 
   static std::unique_ptr<FakeOutputSurface> CreateOffscreen(
-      scoped_refptr<ContextProvider> context_provider) {
+      scoped_refptr<viz::ContextProvider> context_provider) {
     auto surface =
         base::WrapUnique(new FakeOutputSurface(std::move(context_provider)));
     surface->capabilities_.uses_default_gl_framebuffer = false;
@@ -103,7 +103,8 @@
   }
 
  protected:
-  explicit FakeOutputSurface(scoped_refptr<ContextProvider> context_provider);
+  explicit FakeOutputSurface(
+      scoped_refptr<viz::ContextProvider> context_provider);
   explicit FakeOutputSurface(
       std::unique_ptr<SoftwareOutputDevice> software_device);
 
diff --git a/cc/test/fake_resource_provider.h b/cc/test/fake_resource_provider.h
index f8771fd5..20295b5 100644
--- a/cc/test/fake_resource_provider.h
+++ b/cc/test/fake_resource_provider.h
@@ -17,7 +17,7 @@
 class FakeResourceProvider : public ResourceProvider {
  public:
   static std::unique_ptr<FakeResourceProvider> Create(
-      ContextProvider* context_provider,
+      viz::ContextProvider* context_provider,
       viz::SharedBitmapManager* shared_bitmap_manager) {
     viz::ResourceSettings resource_settings;
     resource_settings.texture_id_allocation_chunk_size = 1;
@@ -29,7 +29,7 @@
   }
 
   static std::unique_ptr<FakeResourceProvider> Create(
-      ContextProvider* context_provider,
+      viz::ContextProvider* context_provider,
       viz::SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
     viz::ResourceSettings resource_settings;
@@ -42,7 +42,7 @@
   }
 
  private:
-  FakeResourceProvider(ContextProvider* context_provider,
+  FakeResourceProvider(viz::ContextProvider* context_provider,
                        viz::SharedBitmapManager* shared_bitmap_manager,
                        gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
                        BlockingTaskRunner* blocking_main_thread_task_runner,
diff --git a/cc/test/layer_tree_pixel_resource_test.cc b/cc/test/layer_tree_pixel_resource_test.cc
index 0088515..f8d935f 100644
--- a/cc/test/layer_tree_pixel_resource_test.cc
+++ b/cc/test/layer_tree_pixel_resource_test.cc
@@ -128,9 +128,9 @@
   DCHECK(task_runner);
   DCHECK(initialized_);
 
-  ContextProvider* compositor_context_provider =
+  viz::ContextProvider* compositor_context_provider =
       host_impl->layer_tree_frame_sink()->context_provider();
-  ContextProvider* worker_context_provider =
+  viz::ContextProvider* worker_context_provider =
       host_impl->layer_tree_frame_sink()->worker_context_provider();
   ResourceProvider* resource_provider = host_impl->resource_provider();
   int max_bytes_per_copy_operation = 1024 * 1024;
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index 345d890..a3a6eee66 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -42,8 +42,8 @@
 LayerTreePixelTest::CreateLayerTreeFrameSink(
     const RendererSettings& renderer_settings,
     double refresh_rate,
-    scoped_refptr<ContextProvider>,
-    scoped_refptr<ContextProvider>) {
+    scoped_refptr<viz::ContextProvider>,
+    scoped_refptr<viz::ContextProvider>) {
   scoped_refptr<TestInProcessContextProvider> compositor_context_provider;
   scoped_refptr<TestInProcessContextProvider> worker_context_provider;
   if (test_type_ == PIXEL_TEST_GL) {
@@ -68,7 +68,7 @@
 
 std::unique_ptr<OutputSurface>
 LayerTreePixelTest::CreateDisplayOutputSurfaceOnThread(
-    scoped_refptr<ContextProvider> compositor_context_provider) {
+    scoped_refptr<viz::ContextProvider> compositor_context_provider) {
   std::unique_ptr<PixelTestOutputSurface> display_output_surface;
   if (test_type_ == PIXEL_TEST_GL) {
     // Pixel tests use a separate context for the Display to more closely
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h
index 4e15faf..246eddf 100644
--- a/cc/test/layer_tree_pixel_test.h
+++ b/cc/test/layer_tree_pixel_test.h
@@ -42,10 +42,10 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override;
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override;
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override;
+      scoped_refptr<viz::ContextProvider> compositor_context_provider) override;
 
   virtual std::unique_ptr<CopyOutputRequest> CreateCopyOutputRequest();
 
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 51131b2..f2131f381 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -474,7 +474,8 @@
 
   // viz::TestLayerTreeFrameSinkClient implementation.
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurface(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     return hooks_->CreateDisplayOutputSurfaceOnThread(
         std::move(compositor_context_provider));
   }
@@ -878,8 +879,8 @@
 LayerTreeTest::CreateLayerTreeFrameSink(
     const RendererSettings& renderer_settings,
     double refresh_rate,
-    scoped_refptr<ContextProvider> compositor_context_provider,
-    scoped_refptr<ContextProvider> worker_context_provider) {
+    scoped_refptr<viz::ContextProvider> compositor_context_provider,
+    scoped_refptr<viz::ContextProvider> worker_context_provider) {
   constexpr bool disable_display_vsync = false;
   bool synchronous_composite =
       !HasImplThread() &&
@@ -893,7 +894,7 @@
 
 std::unique_ptr<OutputSurface>
 LayerTreeTest::CreateDisplayOutputSurfaceOnThread(
-    scoped_refptr<ContextProvider> compositor_context_provider) {
+    scoped_refptr<viz::ContextProvider> compositor_context_provider) {
   // By default the Display shares a context with the LayerTreeHostImpl.
   return FakeOutputSurface::Create3d(std::move(compositor_context_provider));
 }
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 92aeb44..5230fc60 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -138,21 +138,21 @@
 
   // By default, output surface recreation is synchronous.
   void RequestNewLayerTreeFrameSink() override;
-  // Override this and call the base class to change what ContextProviders will
-  // be used (such as for pixel tests). Or override it and create your own
+  // Override this and call the base class to change what viz::ContextProviders
+  // will be used (such as for pixel tests). Or override it and create your own
   // TestLayerTreeFrameSink to control how it is created.
   virtual std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider);
-  // Override this and call the base class to change what ContextProvider will
-  // be used, such as to prevent sharing the context with the
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider);
+  // Override this and call the base class to change what viz::ContextProvider
+  // will be used, such as to prevent sharing the context with the
   // LayerTreeFrameSink. Or override it and create your own OutputSurface to
   // change what type of OutputSurface is used, such as a real OutputSurface for
   // pixel tests or a software-compositing OutputSurface.
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override;
+      scoped_refptr<viz::ContextProvider> compositor_context_provider) override;
 
   gfx::Vector2dF ScrollDelta(LayerImpl* layer_impl);
 
diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h
index cf285e5..611d489 100644
--- a/cc/test/pixel_test.h
+++ b/cc/test/pixel_test.h
@@ -50,7 +50,7 @@
                                              const PixelComparator& comparator,
                                              const gfx::Rect* copy_rect);
 
-  ContextProvider* context_provider() const {
+  viz::ContextProvider* context_provider() const {
     return output_surface_->context_provider();
   }
 
diff --git a/cc/test/pixel_test_output_surface.cc b/cc/test/pixel_test_output_surface.cc
index a0caa4a6..5ee44fb 100644
--- a/cc/test/pixel_test_output_surface.cc
+++ b/cc/test/pixel_test_output_surface.cc
@@ -17,7 +17,7 @@
 namespace cc {
 
 PixelTestOutputSurface::PixelTestOutputSurface(
-    scoped_refptr<ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     bool flipped_output_surface)
     : OutputSurface(std::move(context_provider)), weak_ptr_factory_(this) {
   capabilities_.flipped_output_surface = flipped_output_surface;
diff --git a/cc/test/pixel_test_output_surface.h b/cc/test/pixel_test_output_surface.h
index b861d203..9841ede 100644
--- a/cc/test/pixel_test_output_surface.h
+++ b/cc/test/pixel_test_output_surface.h
@@ -13,7 +13,7 @@
 class PixelTestOutputSurface : public OutputSurface {
  public:
   explicit PixelTestOutputSurface(
-      scoped_refptr<ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
       bool flipped_output_surface);
   explicit PixelTestOutputSurface(
       std::unique_ptr<SoftwareOutputDevice> software_device);
diff --git a/cc/test/test_context_provider.cc b/cc/test/test_context_provider.cc
index a34e731a7..49444a9 100644
--- a/cc/test/test_context_provider.cc
+++ b/cc/test/test_context_provider.cc
@@ -15,9 +15,9 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/test/test_gles2_interface.h"
 #include "cc/test/test_web_graphics_context_3d.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
@@ -153,10 +153,11 @@
   context_thread_checker_.DetachFromThread();
   context_gl_->set_test_context(context3d_.get());
   context3d_->set_test_support(support_.get());
-  // Just pass nullptr to the ContextCacheController for its task runner. Idle
-  // handling is tested directly in ContextCacheController's unittests, and
-  // isn't needed here.
-  cache_controller_.reset(new ContextCacheController(support_.get(), nullptr));
+  // Just pass nullptr to the viz::ContextCacheController for its task runner.
+  // Idle handling is tested directly in viz::ContextCacheController's
+  // unittests, and isn't needed here.
+  cache_controller_.reset(
+      new viz::ContextCacheController(support_.get(), nullptr));
 }
 
 TestContextProvider::~TestContextProvider() {
@@ -223,7 +224,7 @@
   return gr_context_->get();
 }
 
-ContextCacheController* TestContextProvider::CacheController() {
+viz::ContextCacheController* TestContextProvider::CacheController() {
   DCHECK(context_thread_checker_.CalledOnValidThread());
   return cache_controller_.get();
 }
diff --git a/cc/test/test_context_provider.h b/cc/test/test_context_provider.h
index 7d47431..867de8c 100644
--- a/cc/test/test_context_provider.h
+++ b/cc/test/test_context_provider.h
@@ -15,8 +15,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
-#include "cc/output/context_provider.h"
 #include "cc/test/test_context_support.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface_stub.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
@@ -28,7 +28,7 @@
 class TestWebGraphicsContext3D;
 class TestGLES2Interface;
 
-class TestContextProvider : public ContextProvider {
+class TestContextProvider : public viz::ContextProvider {
  public:
   typedef base::Callback<std::unique_ptr<TestWebGraphicsContext3D>(void)>
       CreateCallback;
@@ -51,7 +51,7 @@
   gpu::gles2::GLES2Interface* ContextGL() override;
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
-  ContextCacheController* CacheController() override;
+  viz::ContextCacheController* CacheController() override;
   void InvalidateGrContext(uint32_t state) override;
   base::Lock* GetLock() override;
   void SetLostContextCallback(const LostContextCallback& cb) override;
@@ -80,7 +80,7 @@
   std::unique_ptr<TestWebGraphicsContext3D> context3d_;
   std::unique_ptr<TestGLES2Interface> context_gl_;
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-  std::unique_ptr<ContextCacheController> cache_controller_;
+  std::unique_ptr<viz::ContextCacheController> cache_controller_;
   bool bound_ = false;
 
   base::ThreadChecker main_thread_checker_;
diff --git a/cc/test/test_hooks.h b/cc/test/test_hooks.h
index c2cc40fb..c8690d6 100644
--- a/cc/test/test_hooks.h
+++ b/cc/test/test_hooks.h
@@ -126,7 +126,7 @@
   // overridden.
   virtual void RequestNewLayerTreeFrameSink() = 0;
   virtual std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) = 0;
+      scoped_refptr<viz::ContextProvider> compositor_context_provider) = 0;
 };
 
 }  // namespace cc
diff --git a/cc/test/test_in_process_context_provider.cc b/cc/test/test_in_process_context_provider.cc
index f70d68c..441bc7a 100644
--- a/cc/test/test_in_process_context_provider.cc
+++ b/cc/test/test_in_process_context_provider.cc
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "cc/output/context_cache_controller.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "components/viz/common/resources/platform_color.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
@@ -64,7 +64,7 @@
       &gpu_memory_buffer_manager_, &image_factory_,
       (shared_context ? shared_context->context_.get() : nullptr),
       base::ThreadTaskRunnerHandle::Get());
-  cache_controller_.reset(new ContextCacheController(
+  cache_controller_.reset(new viz::ContextCacheController(
       context_->GetImplementation(), base::ThreadTaskRunnerHandle::Get()));
 }
 
@@ -93,7 +93,7 @@
   return gr_context_->get();
 }
 
-ContextCacheController* TestInProcessContextProvider::CacheController() {
+viz::ContextCacheController* TestInProcessContextProvider::CacheController() {
   return cache_controller_.get();
 }
 
diff --git a/cc/test/test_in_process_context_provider.h b/cc/test/test_in_process_context_provider.h
index 0b8ea12..19f0ec3 100644
--- a/cc/test/test_in_process_context_provider.h
+++ b/cc/test/test_in_process_context_provider.h
@@ -11,9 +11,9 @@
 
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
-#include "cc/output/context_provider.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_image_factory.h"
+#include "components/viz/common/gpu/context_provider.h"
 
 class GrContext;
 
@@ -34,7 +34,7 @@
     gpu::GLInProcessContext* shared_context,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
-class TestInProcessContextProvider : public ContextProvider {
+class TestInProcessContextProvider : public viz::ContextProvider {
  public:
   explicit TestInProcessContextProvider(
       TestInProcessContextProvider* shared_context);
@@ -43,7 +43,7 @@
   gpu::gles2::GLES2Interface* ContextGL() override;
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
-  ContextCacheController* CacheController() override;
+  viz::ContextCacheController* CacheController() override;
   void InvalidateGrContext(uint32_t state) override;
   base::Lock* GetLock() override;
   gpu::Capabilities ContextCapabilities() override;
@@ -59,7 +59,7 @@
   TestImageFactory image_factory_;
   std::unique_ptr<gpu::GLInProcessContext> context_;
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-  std::unique_ptr<ContextCacheController> cache_controller_;
+  std::unique_ptr<viz::ContextCacheController> cache_controller_;
   base::Lock context_lock_;
 };
 
diff --git a/cc/test/test_web_graphics_context_3d.h b/cc/test/test_web_graphics_context_3d.h
index b7ae1c2..02c6bffa 100644
--- a/cc/test/test_web_graphics_context_3d.h
+++ b/cc/test/test_web_graphics_context_3d.h
@@ -20,9 +20,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/stl_util.h"
 #include "base/synchronization/lock.h"
-#include "cc/output/context_provider.h"
 #include "cc/test/ordered_texture_map.h"
 #include "cc/test/test_texture.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 41511d2c..5a74b79 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -18,9 +18,9 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "cc/base/devtools_instrumentation.h"
-#include "cc/output/context_provider.h"
 #include "cc/raster/tile_task.h"
 #include "cc/tiles/mipmap_util.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -392,7 +392,7 @@
   DCHECK(!upload.image());
 }
 
-GpuImageDecodeCache::GpuImageDecodeCache(ContextProvider* context,
+GpuImageDecodeCache::GpuImageDecodeCache(viz::ContextProvider* context,
                                          viz::ResourceFormat decode_format,
                                          size_t max_working_set_bytes,
                                          size_t max_cache_bytes)
@@ -406,7 +406,7 @@
   // Acquire the context_lock so that we can safely retrieve the
   // GrContextThreadSafeProxy. This proxy can then be used with no lock held.
   {
-    ContextProvider::ScopedContextLock context_lock(context_);
+    viz::ContextProvider::ScopedContextLock context_lock(context_);
     context_threadsafe_proxy_ = sk_sp<GrContextThreadSafeProxy>(
         context->GrContext()->threadSafeProxy());
   }
@@ -622,7 +622,7 @@
                "GpuImageDecodeCache::SetShouldAggressivelyFreeResources",
                "agressive_free_resources", aggressively_free_resources);
   if (aggressively_free_resources) {
-    ContextProvider::ScopedContextLock context_lock(context_);
+    viz::ContextProvider::ScopedContextLock context_lock(context_);
     base::AutoLock lock(lock_);
     // We want to keep as little in our cache as possible. Set our memory limit
     // to zero and EnsureCapacity to clean up memory.
@@ -766,7 +766,7 @@
 void GpuImageDecodeCache::UploadImage(const DrawImage& draw_image) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                "GpuImageDecodeCache::UploadImage");
-  ContextProvider::ScopedContextLock context_lock(context_);
+  viz::ContextProvider::ScopedContextLock context_lock(context_);
   base::AutoLock lock(lock_);
   ImageData* image_data = GetImageDataForDrawImage(draw_image);
   DCHECK(image_data);
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index b290faf..0499472 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -19,9 +19,11 @@
 #include "components/viz/common/quads/resource_format.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
-namespace cc {
-
+namespace viz {
 class ContextProvider;
+}
+
+namespace cc {
 
 // OVERVIEW:
 //
@@ -101,7 +103,7 @@
  public:
   enum class DecodeTaskType { PART_OF_UPLOAD_TASK, STAND_ALONE_DECODE_TASK };
 
-  explicit GpuImageDecodeCache(ContextProvider* context,
+  explicit GpuImageDecodeCache(viz::ContextProvider* context,
                                viz::ResourceFormat decode_format,
                                size_t max_working_set_bytes,
                                size_t max_cache_bytes);
@@ -348,7 +350,7 @@
   void DeletePendingImages();
 
   const viz::ResourceFormat format_;
-  ContextProvider* context_;
+  viz::ContextProvider* context_;
   sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_;
 
   // All members below this point must only be accessed while holding |lock_|.
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index 721f5b1..6c13daf 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -27,7 +27,7 @@
 size_t kGpuMemoryLimitBytes = 96 * 1024 * 1024;
 class TestGpuImageDecodeCache : public GpuImageDecodeCache {
  public:
-  explicit TestGpuImageDecodeCache(ContextProvider* context,
+  explicit TestGpuImageDecodeCache(viz::ContextProvider* context,
                                    viz::ResourceFormat format)
       : GpuImageDecodeCache(context,
                             format,
@@ -651,7 +651,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -686,7 +686,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -722,7 +722,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -769,7 +769,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -826,7 +826,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -873,7 +873,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -910,7 +910,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -949,7 +949,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -989,7 +989,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -1022,7 +1022,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_TRUE(decoded_draw_image.image());
@@ -1065,7 +1065,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_FALSE(decoded_draw_image.image());
@@ -1095,7 +1095,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
   DecodedDrawImage decoded_draw_image =
       cache.GetDecodedImageForDraw(draw_image);
   EXPECT_FALSE(decoded_draw_image.image());
@@ -1389,7 +1389,7 @@
 
   // Must hold context lock before calling GetDecodedImageForDraw /
   // DrawWithImageFinished.
-  ContextProvider::ScopedContextLock context_lock(context_provider.get());
+  viz::ContextProvider::ScopedContextLock context_lock(context_provider.get());
 
   // Do an at-raster decode of the above image that *does* require mips.
   DrawImage draw_image_mips(
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index cf65cad..e43d7a93 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -682,7 +682,7 @@
 
   bool gpu_rasterization_enabled = false;
   if (host_impl->layer_tree_frame_sink()) {
-    ContextProvider* compositor_context_provider =
+    viz::ContextProvider* compositor_context_provider =
         host_impl->layer_tree_frame_sink()->context_provider();
     if (compositor_context_provider) {
       gpu_rasterization_enabled =
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index cd185fa..a546dc9 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1837,9 +1837,9 @@
         layer_tree_frame_sink_->worker_context_provider()))
     return false;
 
-  ContextProvider* context_provider =
+  viz::ContextProvider* context_provider =
       layer_tree_frame_sink_->worker_context_provider();
-  ContextProvider::ScopedContextLock scoped_context(context_provider);
+  viz::ContextProvider::ScopedContextLock scoped_context(context_provider);
   if (!context_provider->GrContext())
     return false;
 
@@ -1856,7 +1856,7 @@
 
   int requested_msaa_samples = RequestedMSAASampleCount();
   int max_msaa_samples = 0;
-  ContextProvider* compositor_context_provider =
+  viz::ContextProvider* compositor_context_provider =
       layer_tree_frame_sink_->context_provider();
   bool gpu_rasterization_enabled = false;
   bool supports_disable_msaa = false;
@@ -2022,7 +2022,7 @@
 
 void LayerTreeHostImpl::DidLoseLayerTreeFrameSink() {
   if (resource_provider_)
-    resource_provider_->DidLoseContextProvider();
+    resource_provider_->DidLoseVulkanContextProvider();
   has_valid_layer_tree_frame_sink_ = false;
   client_->DidLoseLayerTreeFrameSinkOnImplThread();
 }
@@ -2341,7 +2341,7 @@
   // resolved.
   CHECK(resource_provider_);
 
-  ContextProvider* compositor_context_provider =
+  viz::ContextProvider* compositor_context_provider =
       layer_tree_frame_sink_->context_provider();
   if (!compositor_context_provider) {
     *resource_pool =
@@ -2355,7 +2355,7 @@
     return;
   }
 
-  ContextProvider* worker_context_provider =
+  viz::ContextProvider* worker_context_provider =
       layer_tree_frame_sink_->worker_context_provider();
   if (use_gpu_rasterization_) {
     DCHECK(worker_context_provider);
@@ -2483,7 +2483,7 @@
       compositor_context->ContextGL()->ShallowFlushCHROMIUM();
     if (auto* worker_context =
             layer_tree_frame_sink_->worker_context_provider()) {
-      ContextProvider::ScopedContextLock hold(worker_context);
+      viz::ContextProvider::ScopedContextLock hold(worker_context);
       worker_context->ContextGL()->ShallowFlushCHROMIUM();
     }
   }
@@ -4389,7 +4389,7 @@
   // before we get a chance to go invisible in NotifyAllTileTasksComplete.
   auto* worker_context = layer_tree_frame_sink_->worker_context_provider();
   if (worker_context && is_visible != !!worker_context_visibility_) {
-    ContextProvider::ScopedContextLock hold(worker_context);
+    viz::ContextProvider::ScopedContextLock hold(worker_context);
     if (is_visible) {
       worker_context_visibility_ =
           worker_context->CacheController()->ClientBecameVisible();
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 1f809c49..c1a3920 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -26,7 +26,6 @@
 #include "cc/input/scrollbar_animation_controller.h"
 #include "cc/layers/layer_collections.h"
 #include "cc/output/begin_frame_args.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/output/layer_tree_frame_sink_client.h"
 #include "cc/output/managed_memory_policy.h"
 #include "cc/quads/render_pass.h"
@@ -43,6 +42,7 @@
 #include "cc/trees/layer_tree_settings.h"
 #include "cc/trees/mutator_host_client.h"
 #include "cc/trees/task_runner_provider.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "ui/gfx/geometry/rect.h"
@@ -750,11 +750,11 @@
 
   // The following scoped variables must not outlive the
   // |layer_tree_frame_sink_|.
-  // These should be transfered to ContextCacheController's
+  // These should be transfered to viz::ContextCacheController's
   // ClientBecameNotVisible() before the output surface is destroyed.
-  std::unique_ptr<ContextCacheController::ScopedVisibility>
+  std::unique_ptr<viz::ContextCacheController::ScopedVisibility>
       compositor_context_visibility_;
-  std::unique_ptr<ContextCacheController::ScopedVisibility>
+  std::unique_ptr<viz::ContextCacheController::ScopedVisibility>
       worker_context_visibility_;
 
   std::unique_ptr<ResourceProvider> resource_provider_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index adb20a5..dd5579d 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -8785,11 +8785,12 @@
 class FrameSinkClient : public viz::TestLayerTreeFrameSinkClient {
  public:
   explicit FrameSinkClient(
-      scoped_refptr<ContextProvider> display_context_provider)
+      scoped_refptr<viz::ContextProvider> display_context_provider)
       : display_context_provider_(std::move(display_context_provider)) {}
 
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurface(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     return FakeOutputSurface::Create3d(std::move(display_context_provider_));
   }
 
@@ -8801,7 +8802,7 @@
   void DisplayDidDrawAndSwap() override {}
 
  private:
-  scoped_refptr<ContextProvider> display_context_provider_;
+  scoped_refptr<viz::ContextProvider> display_context_provider_;
 };
 
 TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) {
@@ -8833,7 +8834,7 @@
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  // The CopyOutputResult's callback has a ref on the ContextProvider and a
+  // The CopyOutputResult's callback has a ref on the viz::ContextProvider and a
   // texture in a texture mailbox.
   EXPECT_FALSE(context_provider->HasOneRef());
   EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc
index f1c354b..8e50cb0 100644
--- a/cc/trees/layer_tree_host_perftest.cc
+++ b/cc/trees/layer_tree_host_perftest.cc
@@ -49,8 +49,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     constexpr bool disable_display_vsync = true;
     bool synchronous_composite =
         !HasImplThread() &&
diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc
index b8a5018..47f69d9 100644
--- a/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -72,8 +72,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     RendererSettings modified_renderer_settings = renderer_settings;
     modified_renderer_settings.force_antialiasing = force_antialiasing_;
     modified_renderer_settings.force_blending_with_shaders =
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc
index 58d1140e..2617a12 100644
--- a/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -177,12 +177,12 @@
   void WillPrepareTilesOnThread(LayerTreeHostImpl* host_impl) override {
     // Issue a GL finish before preparing tiles to ensure resources become
     // available for use in a timely manner. Needed for the one-copy path.
-    ContextProvider* context_provider =
+    viz::ContextProvider* context_provider =
         host_impl->layer_tree_frame_sink()->worker_context_provider();
     if (!context_provider)
       return;
 
-    ContextProvider::ScopedContextLock lock(context_provider);
+    viz::ContextProvider::ScopedContextLock lock(context_provider);
     lock.ContextGL()->Finish();
   }
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 5ed5c36..1d9f172c 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -444,15 +444,15 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
-    // Create the main ContextProvider with a MockContextSupport.
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
+    // Create the main viz::ContextProvider with a MockContextSupport.
     auto main_support = base::MakeUnique<MockContextSupport>();
     mock_main_context_support_ = main_support.get();
     auto test_main_context_provider = TestContextProvider::Create(
         TestWebGraphicsContext3D::Create(), std::move(main_support));
 
-    // Create the main ContextProvider with a MockContextSupport.
+    // Create the main viz::ContextProvider with a MockContextSupport.
     auto worker_support = base::MakeUnique<MockContextSupport>();
     mock_worker_context_support_ = worker_support.get();
     auto test_worker_context_provider = TestContextProvider::Create(
@@ -3363,8 +3363,8 @@
 class OnDrawLayerTreeFrameSink : public viz::TestLayerTreeFrameSink {
  public:
   OnDrawLayerTreeFrameSink(
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider,
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider,
       viz::SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       const RendererSettings& renderer_settings,
@@ -3407,8 +3407,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     auto on_draw_callback = base::Bind(
         &LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor::
             CallOnDraw,
@@ -5538,8 +5538,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     auto context = TestWebGraphicsContext3D::Create();
     context->SetMaxSamples(4);
     context->set_support_multisample_compatibility(false);
@@ -6068,8 +6068,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     constexpr bool disable_display_vsync = false;
     bool synchronous_composite =
         !HasImplThread() &&
@@ -6393,7 +6393,8 @@
     : public LayerTreeHostTestCrispUpAfterPinchEnds {
  protected:
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     scoped_refptr<TestContextProvider> display_context_provider =
         TestContextProvider::Create();
     TestWebGraphicsContext3D* context3d =
@@ -7628,10 +7629,10 @@
     // this here to ensure that our otuput surface exists.
 
     // Retrieve max texture size from Skia.
-    ContextProvider* context_provider =
+    viz::ContextProvider* context_provider =
         host_impl->layer_tree_frame_sink()->context_provider();
     ASSERT_TRUE(context_provider);
-    ContextProvider::ScopedContextLock context_lock(context_provider);
+    viz::ContextProvider::ScopedContextLock context_lock(context_provider);
 
     GrContext* gr_context = context_provider->GrContext();
     ASSERT_TRUE(gr_context);
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index e14401b..55510ac6 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -80,8 +80,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     base::AutoLock lock(context3d_lock_);
 
     std::unique_ptr<TestWebGraphicsContext3D> compositor_context3d =
@@ -1627,7 +1627,7 @@
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
   void WillPrepareTilesOnThread(LayerTreeHostImpl* host_impl) override {
-    ContextProvider::ScopedContextLock scoped_context(
+    viz::ContextProvider::ScopedContextLock scoped_context(
         host_impl->layer_tree_frame_sink()->worker_context_provider());
     gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL();
     gl->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
diff --git a/cc/trees/layer_tree_host_unittest_copyrequest.cc b/cc/trees/layer_tree_host_unittest_copyrequest.cc
index 22f5ca1..b190d8a 100644
--- a/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -129,7 +129,8 @@
   void AfterTest() override { EXPECT_EQ(4u, callbacks_.size()); }
 
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     if (!use_gl_renderer_) {
       return FakeOutputSurface::CreateSoftware(
           base::WrapUnique(new SoftwareOutputDevice));
@@ -466,8 +467,8 @@
   std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink(
       const RendererSettings& renderer_settings,
       double refresh_rate,
-      scoped_refptr<ContextProvider> compositor_context_provider,
-      scoped_refptr<ContextProvider> worker_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider) override {
     auto frame_sink = LayerTreeHostCopyRequestTest::CreateLayerTreeFrameSink(
         renderer_settings, refresh_rate, std::move(compositor_context_provider),
         std::move(worker_context_provider));
@@ -738,7 +739,8 @@
     : public LayerTreeHostCopyRequestTest {
  protected:
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     display_context_provider_ = TestContextProvider::Create();
     display_context_provider_->BindToCurrentThread();
     return FakeOutputSurface::Create3d(display_context_provider_);
@@ -864,7 +866,8 @@
   }
 
   std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
-      scoped_refptr<ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     // These tests expect the LayerTreeHostImpl to share a context with
     // the Display so that sync points are not needed and the texture counts
     // are visible together.
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index dd71d11..9256bbe0 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1310,7 +1310,7 @@
   return layer_tree_host_impl_->debug_state();
 }
 
-ContextProvider* LayerTreeImpl::context_provider() const {
+viz::ContextProvider* LayerTreeImpl::context_provider() const {
   return layer_tree_host_impl_->layer_tree_frame_sink()->context_provider();
 }
 
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 4f7eb398..5c05caf 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -31,9 +31,12 @@
 }
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace cc {
 
-class ContextProvider;
 class DebugRectHistory;
 class FrameRateCounter;
 class HeadsUpDisplayLayerImpl;
@@ -103,7 +106,7 @@
   // ---------------------------------------------------------------------------
   const LayerTreeSettings& settings() const;
   const LayerTreeDebugState& debug_state() const;
-  ContextProvider* context_provider() const;
+  viz::ContextProvider* context_provider() const;
   ResourceProvider* resource_provider() const;
   TileManager* tile_manager() const;
   ImageDecodeCache* image_decode_cache() const;
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 40b7bc7..6fe0a16 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -18,7 +18,6 @@
 #include "cc/base/devtools_instrumentation.h"
 #include "cc/benchmarks/benchmark_instrumentation.h"
 #include "cc/input/browser_controls_offset_manager.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink.h"
 #include "cc/scheduler/compositor_timing_history.h"
 #include "cc/scheduler/delay_based_time_source.h"
@@ -27,6 +26,7 @@
 #include "cc/trees/mutator_host.h"
 #include "cc/trees/proxy_main.h"
 #include "cc/trees/task_runner_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 
 namespace cc {
@@ -209,7 +209,7 @@
   TRACE_EVENT0("cc", "ProxyImpl::FinishGLOnImplThread");
   DCHECK(IsImplThread());
   if (layer_tree_host_impl_->layer_tree_frame_sink()) {
-    ContextProvider* context_provider =
+    viz::ContextProvider* context_provider =
         layer_tree_host_impl_->layer_tree_frame_sink()->context_provider();
     if (context_provider)
       context_provider->ContextGL()->Finish();
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 165f5b42..39b09aa 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -10,7 +10,6 @@
 #include "base/trace_event/trace_event.h"
 #include "cc/base/devtools_instrumentation.h"
 #include "cc/benchmarks/benchmark_instrumentation.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/resources/ui_resource_manager.h"
@@ -24,6 +23,7 @@
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/mutator_host.h"
 #include "cc/trees/scoped_abort_remaining_swap_promises.h"
+#include "components/viz/common/gpu/context_provider.h"
 
 namespace cc {
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index ed453255..bc75575 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -778,7 +778,7 @@
         mToolbarHolder = (FrameLayout) mControlContainer.findViewById(R.id.toolbar_holder);
         mDefaultToolbarView = (BottomToolbarPhone) mControlContainer.findViewById(R.id.toolbar);
 
-        mNtpController = new BottomSheetNewTabController(this, mDefaultToolbarView);
+        mNtpController = new BottomSheetNewTabController(this, mDefaultToolbarView, mActivity);
 
         mActivity.getFullscreenManager().addListener(new FullscreenListener() {
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
index 7d321f806..67c768852 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentController.java
@@ -224,6 +224,16 @@
     }
 
     /**
+     * @param itemId The id of the MenuItem to select.
+     */
+    public void selectItem(int itemId) {
+        // TODO(twellington): A #setSelectedItemId() method was added to the support library
+        //                    recently. Replace this custom implementation with that method after
+        //                    the support library is rolled.
+        onNavigationItemSelected(getMenu().findItem(itemId));
+    }
+
+    /**
      * Shows the specified {@link BottomSheetContent} and opens the {@link BottomSheet}.
      * @param itemId The menu item id of the {@link BottomSheetContent} to show.
      */
@@ -346,17 +356,6 @@
         setItemTextColor(tint);
     }
 
-    /**
-     * @param itemId The id of the MenuItem to select.
-     */
-    @VisibleForTesting
-    public void selectItem(int itemId) {
-        // TODO(twellington): A #setSelectedItemId() method was added to the support library
-        //                    recently. Replace this custom implementation with that method after
-        //                    the support library is rolled.
-        onNavigationItemSelected(getMenu().findItem(itemId));
-    }
-
     @VisibleForTesting
     public int getSelectedItemIdForTests() {
         // TODO(twellington): A #getSelectedItemId() method was added to the support library
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
index e40ad6399..c324efd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.widget.bottomsheet;
 
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
@@ -13,12 +15,11 @@
 
 /**
  * A class that handles showing and hiding the Chrome Home new tab UI.
- *
- * TODO(twellington): Add tests for this class.
  */
 public class BottomSheetNewTabController extends EmptyBottomSheetObserver {
     private final BottomSheet mBottomSheet;
     private final BottomToolbarPhone mToolbar;
+    private final ChromeActivity mActivity;
 
     private LayoutManagerChrome mLayoutManager;
     private OverviewModeObserver mOverviewModeObserver;
@@ -34,11 +35,14 @@
      * @param bottomSheet The {@link BottomSheet} that will be opened as part of the new tab UI.
      * @param toolbar The {@link BottomToolbarPhone} that this controller will set state on as part
      *                of the new tab UI.
+     * @param activity The {@link ChromeActivity} containing the {@link BottomSheet}.
      */
-    public BottomSheetNewTabController(BottomSheet bottomSheet, BottomToolbarPhone toolbar) {
+    public BottomSheetNewTabController(
+            BottomSheet bottomSheet, BottomToolbarPhone toolbar, ChromeActivity activity) {
         mBottomSheet = bottomSheet;
         mBottomSheet.addObserver(this);
         mToolbar = toolbar;
+        mActivity = activity;
     }
 
     /**
@@ -92,14 +96,19 @@
         // Tell the model that a new tab may be added soon.
         mTabModelSelector.getModel(isIncognito).setIsPendingTabAdd(true);
 
-        // Select the correct model, immediately ending animations so that the sheet content is not
-        // in transition while the sheet is opening.
+        // Select the correct model, immediately ending animations so that the previous sheet
+        // content is not in use while calling #setIsPendingTabAdd() on previous model.
         if (mTabModelSelector.isIncognitoSelected() != isIncognito) {
             mTabModelSelector.selectModel(isIncognito);
             mBottomSheet.endTransitionAnimations();
             mTabModelSelector.getModel(!isIncognito).setIsPendingTabAdd(false);
         }
 
+        // Select the correct sheet content, immediately ending animations so that the sheet content
+        // is not in transition while the sheet is opening.
+        mActivity.getBottomSheetContentController().selectItem(R.id.action_home);
+        mBottomSheet.endTransitionAnimations();
+
         // Open the sheet if it isn't already open to the desired height.
         int sheetState = mTabModelSelector.getCurrentModel().getCount() == 0
                 ? BottomSheet.SHEET_STATE_FULL
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index b685bb3..857a0c54 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -192,6 +192,9 @@
       // TEMPORARY: Compositor state for debugging BeginMainFrame renderer hang.
       // TODO(sunnyps): Remove after fixing https://crbug.com/622080
       {kBeginMainFrameHangCompositorState, kSmallSize},
+
+      // TODO(asvitkine): Remove after fixing https://crbug.com/736675
+      {"bad_histogram", kMediumSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 10fd4a7..a1d9afb7 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -903,6 +903,9 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS" desc="In Printing Settings, the title of the CUPS printers setting section.">
       Printers
     </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS_LEARN_MORE_LABEL" desc="Label for the link that teaches users how to use CUPS printing.">
+      Set up or manage CUPS printers.
+    </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER" desc="In CUPS printing settings subpage, text for the link adding a new CUPS printer.">
       Add Printer
     </message>
@@ -913,7 +916,7 @@
       Remove
     </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL" desc="The placeholder text in the printer search field.">
-      printer name
+      Search printers
     </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_NEARBY_TITLE" desc="Text for the title of the dialog that is used to add nearby printers.">
       Add a nearby printer
@@ -1027,9 +1030,6 @@
   <message name="IDS_SETTINGS_PRINTING_CLOUD_PRINTERS" desc="In Printing Settings, the title of the google cloud printers setting section.">
     Google Cloud Print
   </message>
-  <message name="IDS_SETTINGS_PRINTING_CLOUD_PRINTERS_DESCRIPTION" desc="In Printing Settings, the title description of the google cloud printers setting section.">
-    Set up cloud printing devices
-  </message>
 
   <!-- Downloads Page -->
   <message name="IDS_SETTINGS_DOWNLOADS" desc="Name of the settings page which displays download preferences.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 708a342c..0a47df1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3184,10 +3184,6 @@
       "feedback/system_logs/log_sources/crash_ids_source.h",
       "feedback/system_logs/log_sources/memory_details_log_source.cc",
       "feedback/system_logs/log_sources/memory_details_log_source.h",
-      "feedback/system_logs/system_logs_fetcher.cc",
-      "feedback/system_logs/system_logs_fetcher.h",
-      "feedback/system_logs/system_logs_source.cc",
-      "feedback/system_logs/system_logs_source.h",
       "first_run/first_run.cc",
       "first_run/first_run.h",
       "first_run/first_run_dialog.h",
@@ -3402,6 +3398,8 @@
       "resource_coordinator/tab_manager_grc_tab_signal_observer.h",
       "resource_coordinator/tab_manager_observer.cc",
       "resource_coordinator/tab_manager_observer.h",
+      "resource_coordinator/tab_manager_stats_collector.cc",
+      "resource_coordinator/tab_manager_stats_collector.h",
       "resource_coordinator/tab_manager_web_contents_data.cc",
       "resource_coordinator/tab_manager_web_contents_data.h",
       "resource_coordinator/tab_stats.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a8556ccf..d2f4157a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -771,6 +771,8 @@
      switches::kTLS13VariantDraft},
     {flag_descriptions::kTLS13VariantExperiment, switches::kTLS13Variant,
      switches::kTLS13VariantExperiment},
+    {flag_descriptions::kTLS13VariantRecordTypeExperiment,
+     switches::kTLS13Variant, switches::kTLS13VariantRecordTypeExperiment},
 };
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 8f032b7..6ba1166 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "-components/devtools_bridge",
   "+cc/layers/layer.h",
-  "+cc/output/context_provider.h",
   "+chrome_jni_registration/chrome_jni_registration.h",
   "+components/doodle",
   "+components/ntp_snippets",
@@ -9,6 +8,7 @@
   "+components/sync/android",
   "+components/sync/test/fake_server/android",
   "+components/toolbar",
+  "+components/viz/common/gpu/context_provider.h",
   "+components/web_contents_delegate_android",
   "+device/vr/features/features.h",
   "+sandbox/linux/seccomp-bpf/sandbox_bpf.h",
diff --git a/chrome/browser/android/thumbnail/thumbnail_cache.cc b/chrome/browser/android/thumbnail/thumbnail_cache.cc
index 354da956..c9cec7f 100644
--- a/chrome/browser/android/thumbnail/thumbnail_cache.cc
+++ b/chrome/browser/android/thumbnail/thumbnail_cache.cc
@@ -170,7 +170,10 @@
   if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0)
     return;
 
-  DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end());
+  if (thumbnail_meta_data_.find(tab_id) == thumbnail_meta_data_.end()) {
+    DVLOG(1) << "Thumbnail meta data was removed for tab id " << tab_id;
+    return;
+  }
 
   base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time();
   std::unique_ptr<Thumbnail> thumbnail = Thumbnail::Create(
diff --git a/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.cc b/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.cc
index 2fc7c70..14cd5d2 100644
--- a/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.cc
+++ b/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.cc
@@ -10,7 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/browser_thread.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -151,13 +151,13 @@
 
 void MailboxToSurfaceBridge::OnContextAvailable(
     std::unique_ptr<gl::ScopedJavaSurface> surface,
-    scoped_refptr<cc::ContextProvider> provider) {
-  // Must save a reference to the ContextProvider to keep it alive,
+    scoped_refptr<viz::ContextProvider> provider) {
+  // Must save a reference to the viz::ContextProvider to keep it alive,
   // otherwise the GL context created from it becomes invalid.
   context_provider_ = std::move(provider);
 
   if (!context_provider_->BindToCurrentThread()) {
-    DLOG(ERROR) << "Failed to init ContextProvider";
+    DLOG(ERROR) << "Failed to init viz::ContextProvider";
     return;
   }
 
@@ -194,7 +194,7 @@
   auto relay_callback = base::Bind(
       [](scoped_refptr<base::SequencedTaskRunner> runner,
          const content::Compositor::ContextProviderCallback& callback,
-         scoped_refptr<cc::ContextProvider> provider) {
+         scoped_refptr<viz::ContextProvider> provider) {
         runner->PostTask(FROM_HERE, base::Bind(callback, std::move(provider)));
 
       },
diff --git a/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.h b/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.h
index 1704de32..ce94fef 100644
--- a/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.h
+++ b/chrome/browser/android/vr_shell/mailbox_to_surface_bridge.h
@@ -19,7 +19,7 @@
 }
 }
 
-namespace cc {
+namespace viz {
 class ContextProvider;
 }
 
@@ -41,12 +41,12 @@
 
  private:
   void OnContextAvailable(std::unique_ptr<gl::ScopedJavaSurface> surface,
-                          scoped_refptr<cc::ContextProvider>);
+                          scoped_refptr<viz::ContextProvider>);
   void InitializeRenderer();
   void DestroyContext();
   void DrawQuad(unsigned int textureHandle);
 
-  scoped_refptr<cc::ContextProvider> context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
   gpu::gles2::GLES2Interface* gl_ = nullptr;
   int surface_handle_ = 0;
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 95774ff..dd0309b 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -195,6 +195,7 @@
 #include "device/usb/public/interfaces/chooser_service.mojom.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "extensions/features/features.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "gpu/config/gpu_switches.h"
 #include "media/audio/audio_manager.h"
 #include "media/media_features.h"
@@ -1401,6 +1402,16 @@
   return !url.SchemeIs(chrome::kChromeNativeScheme);
 }
 
+std::vector<url::Origin>
+ChromeContentBrowserClient::GetOriginsRequiringDedicatedProcess() {
+  std::vector<url::Origin> isolated_origin_list;
+
+  if (base::FeatureList::IsEnabled(features::kSignInProcessIsolation))
+    isolated_origin_list.emplace_back(GaiaUrls::GetInstance()->gaia_url());
+
+  return isolated_origin_list;
+}
+
 namespace {
 
 bool IsAutoReloadEnabled() {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index dc4aba0..e45c8e2 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -131,6 +131,7 @@
       const GURL& current_url,
       const GURL& new_url) override;
   bool ShouldAssignSiteForURL(const GURL& url) override;
+  std::vector<url::Origin> GetOriginsRequiringDedicatedProcess() override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
   std::string GetApplicationLocale() override;
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index c9251da1..9add49e 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
@@ -11,17 +12,20 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/network_session_configurator/common/network_switches.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/context_menu_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "google_apis/gaia/gaia_switches.h"
 #include "net/dns/mock_host_resolver.h"
 
 class ChromeNavigationBrowserTest : public InProcessBrowserTest {
@@ -436,3 +440,70 @@
   EXPECT_FALSE(navigation_observer.was_same_document());
   EXPECT_FALSE(navigation_observer.was_renderer_initiated());
 }
+
+class SignInIsolationBrowserTest : public ChromeNavigationBrowserTest {
+ public:
+  SignInIsolationBrowserTest()
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+  ~SignInIsolationBrowserTest() override {}
+
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(features::kSignInProcessIsolation);
+    https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
+    ASSERT_TRUE(https_server_.InitializeAndListen());
+    ChromeNavigationBrowserTest::SetUp();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Override the sign-in URL so that it includes correct port from the test
+    // server.
+    command_line->AppendSwitchASCII(
+        ::switches::kGaiaUrl,
+        https_server()->GetURL("accounts.google.com", "/").spec());
+
+    // Ignore cert errors so that the sign-in URL can be loaded from a site
+    // other than localhost (the EmbeddedTestServer serves a certificate that
+    // is valid for localhost).
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+
+    ChromeNavigationBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    https_server_.StartAcceptingConnections();
+    ChromeNavigationBrowserTest::SetUpOnMainThread();
+  }
+
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  net::EmbeddedTestServer https_server_;
+
+  DISALLOW_COPY_AND_ASSIGN(SignInIsolationBrowserTest);
+};
+
+// This test ensures that the sign-in origin requires a dedicated process.  It
+// only ensures that the corresponding base::Feature works properly;
+// IsolatedOriginTest provides the main test coverage of origins whitelisted
+// for process isolation.  See https://crbug.com/739418.
+IN_PROC_BROWSER_TEST_F(SignInIsolationBrowserTest, NavigateToSignInPage) {
+  const GURL first_url =
+      embedded_test_server()->GetURL("google.com", "/title1.html");
+  const GURL signin_url =
+      https_server()->GetURL("accounts.google.com", "/title1.html");
+  ui_test_utils::NavigateToURL(browser(), first_url);
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  scoped_refptr<content::SiteInstance> first_instance(
+      web_contents->GetMainFrame()->GetSiteInstance());
+
+  // Make sure that a renderer-initiated navigation to the sign-in page swaps
+  // processes.
+  content::TestNavigationManager manager(web_contents, signin_url);
+  EXPECT_TRUE(
+      ExecuteScript(web_contents, "location = '" + signin_url.spec() + "';"));
+  manager.WaitForNavigationFinished();
+  EXPECT_NE(web_contents->GetMainFrame()->GetSiteInstance(), first_instance);
+}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d5a988f..1ec4119 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1327,14 +1327,14 @@
     "printing/printer_configurer.h",
     "printing/printer_detector.h",
     "printing/printer_info.h",
-    "printing/printers_manager.cc",
-    "printing/printers_manager.h",
-    "printing/printers_manager_factory.cc",
-    "printing/printers_manager_factory.h",
     "printing/printers_sync_bridge.cc",
     "printing/printers_sync_bridge.h",
     "printing/specifics_translation.cc",
     "printing/specifics_translation.h",
+    "printing/synced_printers_manager.cc",
+    "printing/synced_printers_manager.h",
+    "printing/synced_printers_manager_factory.cc",
+    "printing/synced_printers_manager_factory.h",
     "printing/usb_printer_detector.cc",
     "printing/usb_printer_detector.h",
     "printing/usb_printer_detector_factory.cc",
@@ -1847,8 +1847,8 @@
     "power/renderer_freezer_unittest.cc",
     "preferences_unittest.cc",
     "printing/combining_printer_detector_unittest.cc",
-    "printing/printers_manager_unittest.cc",
     "printing/specifics_translation_unittest.cc",
+    "printing/synced_printers_manager_unittest.cc",
     "profiles/profile_list_chromeos_unittest.cc",
     "proxy_config_service_impl_unittest.cc",
     "resource_reporter/resource_reporter_unittest.cc",
@@ -1887,6 +1887,7 @@
     "../ui/webui/chromeos/login/l10n_util_unittest.cc",
     "../ui/webui/chromeos/login/oobe_display_chooser_unittest.cc",
     "../ui/webui/chromeos/login/signin_userlist_unittest.cc",
+    "../ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc",
     "../ui/webui/settings/chromeos/device_power_handler_unittest.cc",
     "../ui/webui/settings/chromeos/easy_unlock_settings_handler_unittest.cc",
     "//components/drive/change_list_loader_unittest.cc",
diff --git a/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc b/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
index e3888c69a..08b7545 100644
--- a/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
@@ -44,6 +44,73 @@
            ash::mojom::TrayActionState::kAvailable;
   }
 
+  bool RunTestAppInLockScreenContext(const std::string& test_app,
+                                     std::string* error) {
+    scoped_refptr<const extensions::Extension> app =
+        LoadExtension(test_data_dir_.AppendASCII(test_app));
+    if (!app) {
+      *error = "Unable to load the test app.";
+      return false;
+    }
+
+    if (!EnableLockScreenAppLaunch(app->id())) {
+      *error = "Failed to enable app for lock screen.";
+      return false;
+    }
+
+    // The test app will send "readyToClose" message from the app window created
+    // as part of the test. The message will be sent after the tests in the app
+    // window context have been run and the window is ready to be closed.
+    // The test should reply to this message in order for the app window to
+    // close itself.
+    ExtensionTestMessageListener ready_to_close("readyToClose",
+                                                true /* will_reply */);
+
+    extensions::ResultCatcher catcher;
+    lock_screen_apps::StateController::Get()->RequestNewLockScreenNote();
+
+    if (lock_screen_apps::StateController::Get()->GetLockScreenNoteState() !=
+        ash::mojom::TrayActionState::kLaunching) {
+      *error = "App launch request failed";
+      return false;
+    }
+
+    // The test will run two sets of tests:
+    // *  in the window that gets created as the response to the new_note action
+    //    launch
+    // *  in the app background page - the test will launch an app window and
+    //    wait for it to be closed
+    // Test runner should wait for both of those to finish (test result message
+    // will be sent for each set of tests).
+    if (!catcher.GetNextResult()) {
+      *error = catcher.message();
+      if (ready_to_close.was_satisfied())
+        ready_to_close.Reply("failed");
+      return false;
+    }
+
+    if (lock_screen_apps::StateController::Get()->GetLockScreenNoteState() !=
+        ash::mojom::TrayActionState::kActive) {
+      *error = "App not in active state.";
+      return false;
+    }
+
+    if (!ready_to_close.WaitUntilSatisfied()) {
+      *error = "Failed waiting for readyToClose message.";
+      return false;
+    }
+
+    // Close the app window created by the API test.
+    ready_to_close.Reply("close");
+
+    if (!catcher.GetNextResult()) {
+      *error = catcher.message();
+      return false;
+    }
+
+    return true;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LockScreenNoteTakingTest);
 };
@@ -53,42 +120,10 @@
 IN_PROC_BROWSER_TEST_F(LockScreenNoteTakingTest, Launch) {
   ASSERT_TRUE(lock_screen_apps::StateController::IsEnabled());
 
-  scoped_refptr<const extensions::Extension> app =
-      LoadExtension(test_data_dir_.AppendASCII("lock_screen_apps/app_launch"));
-  ASSERT_TRUE(app);
-  ASSERT_TRUE(EnableLockScreenAppLaunch(app->id()));
-
-  // The test app will send "readyToClose" message from the app window created
-  // as part of the test. The message will be sent after the tests in the app
-  // window context have been run and the window is ready to be closed.
-  // The test should reply to this message in order for the app window to close
-  // itself.
-  ExtensionTestMessageListener ready_to_close("readyToClose",
-                                              true /* will_reply */);
-
-  extensions::ResultCatcher catcher;
-  lock_screen_apps::StateController::Get()->RequestNewLockScreenNote();
-
-  ASSERT_EQ(ash::mojom::TrayActionState::kLaunching,
-            lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
-
-  // The test will run two sets of tests:
-  // *  in the window that gets created as the response to the new_note action
-  //    launch
-  // *  in the app background page - the test will launch an app window and wait
-  //    for it to be closed
-  // Test runner should wait for both of those to finish (test result message
-  // will be sent for each set of tests).
-  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
-
-  ASSERT_EQ(ash::mojom::TrayActionState::kActive,
-            lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
-
-  ASSERT_TRUE(ready_to_close.WaitUntilSatisfied());
-  // Close the app window created by the API test.
-  ready_to_close.Reply("close");
-
-  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+  std::string error_message;
+  ASSERT_TRUE(RunTestAppInLockScreenContext("lock_screen_apps/app_launch",
+                                            &error_message))
+      << error_message;
 
   EXPECT_EQ(ash::mojom::TrayActionState::kAvailable,
             lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
@@ -128,3 +163,47 @@
 
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
+
+IN_PROC_BROWSER_TEST_F(LockScreenNoteTakingTest, DataCreation) {
+  ASSERT_TRUE(lock_screen_apps::StateController::IsEnabled());
+
+  std::string error_message;
+  ASSERT_TRUE(RunTestAppInLockScreenContext("lock_screen_apps/data_provider",
+                                            &error_message))
+      << error_message;
+
+  EXPECT_EQ(ash::mojom::TrayActionState::kAvailable,
+            lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
+
+  extensions::ResultCatcher catcher;
+  session_manager::SessionManager::Get()->SetSessionState(
+      session_manager::SessionState::ACTIVE);
+
+  // Unlocking the session should trigger onDataItemsAvailable event, which
+  // should be catched by the background page in the main app - the event should
+  // start another test sequence.
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenNoteTakingTest, PRE_DataAvailableOnRestart) {
+  ASSERT_TRUE(lock_screen_apps::StateController::IsEnabled());
+
+  std::string error_message;
+  ASSERT_TRUE(RunTestAppInLockScreenContext("lock_screen_apps/data_provider",
+                                            &error_message))
+      << error_message;
+
+  EXPECT_EQ(ash::mojom::TrayActionState::kAvailable,
+            lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
+}
+
+IN_PROC_BROWSER_TEST_F(LockScreenNoteTakingTest, DataAvailableOnRestart) {
+  // In PRE_ part  of the test there were data items created in the lock screen
+  // storage - when the lock screen note taking is initialized,
+  // OnDataItemsAvailable should be dispatched to the test app (given that the
+  // lock screen app's data storage is not empty), which should in turn run a
+  // sequence of API tests (in the test app background page).
+  // This test is intended to catch the result of these tests.
+  extensions::ResultCatcher catcher;
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
diff --git a/chrome/browser/chromeos/lock_screen_apps/state_controller.cc b/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
index 07e92a6..f0c02d1 100644
--- a/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
@@ -7,20 +7,26 @@
 #include <utility>
 
 #include "ash/public/interfaces/constants.mojom.h"
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/path_service.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/common/service_manager_connection.h"
+#include "crypto/symmetric_key.h"
+#include "extensions/browser/api/lock_screen_data/lock_screen_item_storage.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/native_app_window.h"
 #include "extensions/common/extension.h"
@@ -34,8 +40,21 @@
 
 namespace {
 
+// Key for user pref that contains the 256 bit AES key that should be used to
+// encrypt persisted user data created on the lock screen.
+constexpr char kDataCryptoKeyPref[] = "lockScreenAppDataCryptoKey";
+
 StateController* g_instance = nullptr;
 
+// Generates a random 256 bit AES key. Returns an empty string on error.
+std::string GenerateCryptoKey() {
+  std::unique_ptr<crypto::SymmetricKey> symmetric_key =
+      crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256);
+  if (!symmetric_key)
+    return "";
+  return symmetric_key->key();
+}
+
 }  // namespace
 
 // static
@@ -50,6 +69,11 @@
   return g_instance;
 }
 
+// static
+void StateController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(kDataCryptoKeyPref, "");
+}
+
 StateController::StateController()
     : binding_(this),
       app_window_observer_(this),
@@ -114,6 +138,7 @@
 
 void StateController::Shutdown() {
   session_observer_.RemoveAll();
+  lock_screen_data_.reset();
   if (app_manager_) {
     app_manager_->Stop();
     ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
@@ -146,11 +171,49 @@
   lock_screen_profile_->GetPrefs()->SetBoolean(prefs::kForceEphemeralProfiles,
                                                true);
 
+  std::string key;
+  if (!GetUserCryptoKey(primary_profile, &key)) {
+    LOG(ERROR) << "Failed to get crypto key for user lock screen apps.";
+    return;
+  }
+
+  InitializeWithCryptoKey(primary_profile, key);
+}
+
+bool StateController::GetUserCryptoKey(Profile* profile, std::string* key) {
+  *key = profile->GetPrefs()->GetString(kDataCryptoKeyPref);
+  if (!key->empty() && base::Base64Decode(*key, key))
+    return true;
+
+  *key = GenerateCryptoKey();
+
+  if (key->empty())
+    return false;
+
+  std::string base64_encoded_key;
+  base::Base64Encode(*key, &base64_encoded_key);
+
+  profile->GetPrefs()->SetString(kDataCryptoKeyPref, base64_encoded_key);
+  return true;
+}
+
+void StateController::InitializeWithCryptoKey(Profile* profile,
+                                              const std::string& crypto_key) {
+  base::FilePath base_path;
+  if (!base::PathService::Get(chrome::DIR_USER_DATA, &base_path)) {
+    LOG(ERROR) << "Failed to get base storage dir for lock screen app data.";
+    return;
+  }
+
+  lock_screen_data_ =
+      base::MakeUnique<extensions::lock_screen_data::LockScreenItemStorage>(
+          profile, g_browser_process->local_state(), crypto_key,
+          base_path.AppendASCII("lock_screen_app_data"));
+
   // App manager might have been set previously by a test.
   if (!app_manager_)
     app_manager_ = base::MakeUnique<AppManagerImpl>();
-  app_manager_->Initialize(primary_profile,
-                           lock_screen_profile->GetOriginalProfile());
+  app_manager_->Initialize(profile, lock_screen_profile_->GetOriginalProfile());
 
   input_devices_observer_.Add(ui::InputDeviceManager::GetInstance());
   power_manager_client_observer_.Add(
@@ -192,6 +255,7 @@
 
 void StateController::OnSessionStateChanged() {
   if (!session_manager::SessionManager::Get()->IsScreenLocked()) {
+    lock_screen_data_->SetSessionLocked(false);
     app_manager_->Stop();
     ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
     return;
@@ -203,6 +267,7 @@
   app_manager_->Start(
       base::Bind(&StateController::OnNoteTakingAvailabilityChanged,
                  base::Unretained(this)));
+  lock_screen_data_->SetSessionLocked(true);
   OnNoteTakingAvailabilityChanged();
 }
 
diff --git a/chrome/browser/chromeos/lock_screen_apps/state_controller.h b/chrome/browser/chromeos/lock_screen_apps/state_controller.h
index 016e220..8893e6fa 100644
--- a/chrome/browser/chromeos/lock_screen_apps/state_controller.h
+++ b/chrome/browser/chromeos/lock_screen_apps/state_controller.h
@@ -6,8 +6,10 @@
 #define CHROME_BROWSER_CHROMEOS_LOCK_SCREEN_APPS_STATE_CONTROLLER_H_
 
 #include <memory>
+#include <string>
 
 #include "ash/public/interfaces/tray_action.mojom.h"
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
@@ -21,6 +23,8 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ui/events/devices/input_device_event_observer.h"
 
+class PrefRegistrySimple;
+
 namespace content {
 class BrowserContext;
 }
@@ -29,6 +33,10 @@
 class AppDelegate;
 class AppWindow;
 class Extension;
+
+namespace lock_screen_data {
+class LockScreenItemStorage;
+}
 }  // namespace extensions
 
 namespace session_manager {
@@ -61,6 +69,8 @@
   // nullptr when lock screen apps are not enabled (see |IsEnabled|).
   static StateController* Get();
 
+  static void RegisterProfilePrefs(PrefRegistrySimple* pref_registry);
+
   // Note that only one StateController is allowed per process. Creating a
   // StateController will set global instance ptr that can be accessed using
   // |Get|. This pointer will be reset when the StateController is destroyed.
@@ -145,6 +155,18 @@
                        Profile* lock_screen_profile,
                        Profile::CreateStatus status);
 
+  // Gets the encryption key that should be used to encrypt user data created on
+  // the lock screen. If a key hadn't previously been created and saved to
+  // user prefs, a new key is created and saved.
+  // |crypto_key| - the found/created key.
+  // Returns whether |crypto_key| was successfully retrieved.
+  bool GetUserCryptoKey(Profile* profile, std::string* crypto_key);
+
+  // Finishes lock screen apps initialization with primary user profile and
+  // associated encryption key to be used for encrypting user data created in
+  // lock screen context.
+  void InitializeWithCryptoKey(Profile* profile, const std::string& crypto_key);
+
   // Called when app manager reports that note taking availability has changed.
   void OnNoteTakingAvailabilityChanged();
 
@@ -172,6 +194,9 @@
 
   Profile* lock_screen_profile_ = nullptr;
 
+  std::unique_ptr<extensions::lock_screen_data::LockScreenItemStorage>
+      lock_screen_data_;
+
   std::unique_ptr<AppManager> app_manager_;
 
   extensions::AppWindow* note_app_window_ = nullptr;
diff --git a/chrome/browser/chromeos/lock_screen_apps/state_controller_unittest.cc b/chrome/browser/chromeos/lock_screen_apps/state_controller_unittest.cc
index 714f20f..958884a 100644
--- a/chrome/browser/chromeos/lock_screen_apps/state_controller_unittest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/state_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/public/interfaces/tray_action.mojom.h"
+#include "base/base64.h"
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/scoped_command_line.h"
@@ -29,8 +30,10 @@
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/api/lock_screen_data/lock_screen_item_storage.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_contents.h"
 #include "extensions/browser/app_window/native_app_window.h"
@@ -45,6 +48,7 @@
 using ash::mojom::TrayActionState;
 using extensions::DictionaryBuilder;
 using extensions::ListBuilder;
+using extensions::lock_screen_data::LockScreenItemStorage;
 
 namespace {
 
@@ -55,6 +59,9 @@
 // The primary tesing profile.
 const char kPrimaryProfileName[] = "primary_profile";
 
+// Key for pref containing lock screen data crypto key.
+constexpr char kDataCryptoKeyPref[] = "lockScreenAppDataCryptoKey";
+
 scoped_refptr<extensions::Extension> CreateTestNoteTakingApp(
     const std::string& app_id) {
   ListBuilder action_handlers;
@@ -339,6 +346,7 @@
     BrowserWithTestWindowTest::SetUp();
 
     session_manager_ = base::MakeUnique<session_manager::SessionManager>();
+    session_manager_->SessionStarted();
     session_manager_->SetSessionState(
         session_manager::SessionState::LOGIN_PRIMARY);
 
@@ -602,6 +610,78 @@
   ExpectObservedStatesMatch({TrayActionState::kAvailable}, "Available on lock");
 }
 
+TEST_F(LockScreenAppStateTest, InitLockScreenDataLockScreenItemStorage) {
+  EXPECT_EQ(TestAppManager::State::kNotInitialized, app_manager()->state());
+  SetPrimaryProfileAndWaitUntilReady();
+
+  LockScreenItemStorage* lock_screen_item_storage =
+      LockScreenItemStorage::GetIfAllowed(profile());
+  ASSERT_TRUE(lock_screen_item_storage);
+
+  std::string crypto_key_in_prefs =
+      profile()->GetPrefs()->GetString(kDataCryptoKeyPref);
+  ASSERT_FALSE(crypto_key_in_prefs.empty());
+  ASSERT_TRUE(base::Base64Decode(crypto_key_in_prefs, &crypto_key_in_prefs));
+
+  EXPECT_EQ(crypto_key_in_prefs,
+            lock_screen_item_storage->crypto_key_for_testing());
+
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
+
+  EXPECT_FALSE(LockScreenItemStorage::GetIfAllowed(profile()));
+  EXPECT_TRUE(LockScreenItemStorage::GetIfAllowed(lock_screen_profile()));
+}
+
+TEST_F(LockScreenAppStateTest,
+       InitLockScreenDataLockScreenItemStorageWhileLocked) {
+  EXPECT_EQ(TestAppManager::State::kNotInitialized, app_manager()->state());
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
+  SetPrimaryProfileAndWaitUntilReady();
+
+  EXPECT_FALSE(LockScreenItemStorage::GetIfAllowed(profile()));
+
+  LockScreenItemStorage* lock_screen_item_storage =
+      LockScreenItemStorage::GetIfAllowed(lock_screen_profile());
+  ASSERT_TRUE(lock_screen_item_storage);
+
+  std::string crypto_key_in_prefs =
+      profile()->GetPrefs()->GetString(kDataCryptoKeyPref);
+  ASSERT_FALSE(crypto_key_in_prefs.empty());
+  ASSERT_TRUE(base::Base64Decode(crypto_key_in_prefs, &crypto_key_in_prefs));
+
+  EXPECT_EQ(crypto_key_in_prefs,
+            lock_screen_item_storage->crypto_key_for_testing());
+
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+
+  EXPECT_TRUE(LockScreenItemStorage::GetIfAllowed(profile()));
+  EXPECT_FALSE(LockScreenItemStorage::GetIfAllowed(lock_screen_profile()));
+}
+
+TEST_F(LockScreenAppStateTest,
+       InitLockScreenDataLockScreenItemStorage_CryptoKeyExists) {
+  std::string crypto_key_in_prefs = "0123456789ABCDEF0123456789ABCDEF";
+  std::string crypto_key_in_prefs_encoded;
+  base::Base64Encode(crypto_key_in_prefs, &crypto_key_in_prefs_encoded);
+
+  profile()->GetPrefs()->SetString(kDataCryptoKeyPref,
+                                   crypto_key_in_prefs_encoded);
+
+  SetPrimaryProfileAndWaitUntilReady();
+
+  LockScreenItemStorage* lock_screen_item_storage =
+      LockScreenItemStorage::GetIfAllowed(profile());
+  ASSERT_TRUE(lock_screen_item_storage);
+
+  EXPECT_EQ(crypto_key_in_prefs,
+            lock_screen_item_storage->crypto_key_for_testing());
+
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
+
+  EXPECT_FALSE(LockScreenItemStorage::GetIfAllowed(profile()));
+  EXPECT_TRUE(LockScreenItemStorage::GetIfAllowed(lock_screen_profile()));
+}
+
 TEST_F(LockScreenAppStateTest, SessionLock) {
   app_manager()->SetInitialAppState(kTestAppId, true);
   SetPrimaryProfileAndWaitUntilReady();
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index efbda30b..2b54234f 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -214,6 +214,7 @@
     chromeos::switches::kEnterpriseDisableArc,
     chromeos::switches::kEnterpriseEnableForcedReEnrollment,
     chromeos::switches::kHasChromeOSDiamondKey,
+    chromeos::switches::kHasChromeOSKeyboard,
     chromeos::switches::kLoginProfile,
     chromeos::switches::kNaturalScrollDefault,
     chromeos::switches::kShowMdLogin,
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_factory.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_factory.cc
index 46b0ab9..a510a06 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_factory.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_factory.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
 
 #include "chrome/browser/chromeos/printing/cups_print_job_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -39,7 +39,7 @@
     : BrowserContextKeyedServiceFactory(
           "CupsPrintJobManagerFactory",
           BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(chromeos::PrintersManagerFactory::GetInstance());
+  DependsOn(chromeos::SyncedPrintersManagerFactory::GetInstance());
 }
 
 CupsPrintJobManagerFactory::~CupsPrintJobManagerFactory() {}
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index f6bcfc0..7f0ad07 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -22,8 +22,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/printing/cups_print_job.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/printing/print_job.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_context.h"
@@ -336,9 +336,8 @@
                       int total_page_number) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-    auto printer =
-        PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter(
-            printer_name);
+    auto printer = SyncedPrintersManagerFactory::GetForBrowserContext(profile_)
+                       ->GetPrinter(printer_name);
     if (!printer) {
       LOG(WARNING)
           << "Printer was removed while job was in progress.  It cannot "
diff --git a/chrome/browser/chromeos/printing/printers_manager_factory.h b/chrome/browser/chromeos/printing/printers_manager_factory.h
deleted file mode 100644
index 0991677..0000000
--- a/chrome/browser/chromeos/printing/printers_manager_factory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_FACTORY_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_FACTORY_H_
-
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace chromeos {
-
-class PrintersManagerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static PrintersManager* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  static PrintersManagerFactory* GetInstance();
-
- protected:
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-
- private:
-  friend struct base::LazyInstanceTraitsBase<PrintersManagerFactory>;
-
-  PrintersManagerFactory();
-  ~PrintersManagerFactory() override;
-
-  // BrowserContextKeyedServiceFactory implementation:
-  PrintersManager* BuildServiceInstanceFor(
-      content::BrowserContext* browser_context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(PrintersManagerFactory);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/printers_manager.cc b/chrome/browser/chromeos/printing/synced_printers_manager.cc
similarity index 86%
rename from chrome/browser/chromeos/printing/printers_manager.cc
rename to chrome/browser/chromeos/printing/synced_printers_manager.cc
index 19d632ae..16e9b9e 100644
--- a/chrome/browser/chromeos/printing/printers_manager.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
 
 #include <memory>
 #include <string>
@@ -51,29 +51,30 @@
 
 }  // anonymous namespace
 
-PrintersManager::PrintersManager(
+SyncedPrintersManager::SyncedPrintersManager(
     Profile* profile,
     std::unique_ptr<PrintersSyncBridge> sync_bridge)
     : profile_(profile), sync_bridge_(std::move(sync_bridge)) {
   pref_change_registrar_.Init(profile->GetPrefs());
   pref_change_registrar_.Add(
       prefs::kRecommendedNativePrinters,
-      base::Bind(&PrintersManager::UpdateRecommendedPrinters,
+      base::Bind(&SyncedPrintersManager::UpdateRecommendedPrinters,
                  base::Unretained(this)));
   UpdateRecommendedPrinters();
 }
 
-PrintersManager::~PrintersManager() {}
+SyncedPrintersManager::~SyncedPrintersManager() {}
 
 // static
-void PrintersManager::RegisterProfilePrefs(
+void SyncedPrintersManager::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterListPref(prefs::kPrintingDevices,
                              user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterListPref(prefs::kRecommendedNativePrinters);
 }
 
-std::vector<std::unique_ptr<Printer>> PrintersManager::GetPrinters() const {
+std::vector<std::unique_ptr<Printer>> SyncedPrintersManager::GetPrinters()
+    const {
   std::vector<std::unique_ptr<Printer>> printers;
 
   std::vector<sync_pb::PrinterSpecifics> values =
@@ -85,8 +86,8 @@
   return printers;
 }
 
-std::vector<std::unique_ptr<Printer>> PrintersManager::GetRecommendedPrinters()
-    const {
+std::vector<std::unique_ptr<Printer>>
+SyncedPrintersManager::GetRecommendedPrinters() const {
   std::vector<std::unique_ptr<Printer>> printers;
 
   for (const std::string& key : recommended_printer_ids_) {
@@ -99,7 +100,7 @@
   return printers;
 }
 
-std::unique_ptr<Printer> PrintersManager::GetPrinter(
+std::unique_ptr<Printer> SyncedPrintersManager::GetPrinter(
     const std::string& printer_id) const {
   // check for a policy printer first
   const auto& policy_printers = recommended_printers_;
@@ -114,7 +115,7 @@
   return printer.has_value() ? printing::SpecificsToPrinter(*printer) : nullptr;
 }
 
-void PrintersManager::RegisterPrinter(std::unique_ptr<Printer> printer) {
+void SyncedPrintersManager::RegisterPrinter(std::unique_ptr<Printer> printer) {
   if (printer->id().empty()) {
     printer->set_id(base::GenerateGUID());
   }
@@ -134,7 +135,7 @@
   }
 }
 
-bool PrintersManager::RemovePrinter(const std::string& printer_id) {
+bool SyncedPrintersManager::RemovePrinter(const std::string& printer_id) {
   DCHECK(!printer_id.empty());
 
   base::Optional<sync_pb::PrinterSpecifics> printer =
@@ -155,21 +156,21 @@
   return success;
 }
 
-void PrintersManager::AddObserver(Observer* observer) {
+void SyncedPrintersManager::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
 
-void PrintersManager::RemoveObserver(Observer* observer) {
+void SyncedPrintersManager::RemoveObserver(Observer* observer) {
   observers_.RemoveObserver(observer);
 }
 
-PrintersSyncBridge* PrintersManager::GetSyncBridge() {
+PrintersSyncBridge* SyncedPrintersManager::GetSyncBridge() {
   return sync_bridge_.get();
 }
 
 // This method is not thread safe and could interact poorly with readers of
 // |recommended_printers_|.
-void PrintersManager::UpdateRecommendedPrinters() {
+void SyncedPrintersManager::UpdateRecommendedPrinters() {
   const PrefService* prefs = profile_->GetPrefs();
 
   const base::ListValue* values =
@@ -227,12 +228,13 @@
   recommended_printers_.swap(new_printers);
 }
 
-void PrintersManager::PrinterInstalled(const Printer& printer) {
+void SyncedPrintersManager::PrinterInstalled(const Printer& printer) {
   DCHECK(!printer.last_updated().is_null());
   installed_printer_timestamps_[printer.id()] = printer.last_updated();
 }
 
-bool PrintersManager::IsConfigurationCurrent(const Printer& printer) const {
+bool SyncedPrintersManager::IsConfigurationCurrent(
+    const Printer& printer) const {
   auto found = installed_printer_timestamps_.find(printer.id());
   if (found == installed_printer_timestamps_.end())
     return false;
diff --git a/chrome/browser/chromeos/printing/printers_manager.h b/chrome/browser/chromeos/printing/synced_printers_manager.h
similarity index 82%
rename from chrome/browser/chromeos/printing/printers_manager.h
rename to chrome/browser/chromeos/printing/synced_printers_manager.h
index 6e05cddd..5a537f1 100644
--- a/chrome/browser/chromeos/printing/printers_manager.h
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_H_
 
 #include <map>
 #include <memory>
@@ -26,10 +26,11 @@
 
 namespace chromeos {
 
-// Manages printer information.  Provides an interface to a user's printers and
+// Manages information about synced local printers classes (CONFIGURED
+// and ENTERPRISE).  Provides an interface to a user's printers and
 // printers provided by policy.  User printers are backed by the
 // PrintersSyncBridge.
-class PrintersManager : public KeyedService {
+class SyncedPrintersManager : public KeyedService {
  public:
   class Observer {
    public:
@@ -38,9 +39,9 @@
     virtual void OnPrinterRemoved(const Printer& printer) = 0;
   };
 
-  PrintersManager(Profile* profile,
-                  std::unique_ptr<PrintersSyncBridge> sync_bridge);
-  ~PrintersManager() override;
+  SyncedPrintersManager(Profile* profile,
+                        std::unique_ptr<PrintersSyncBridge> sync_bridge);
+  ~SyncedPrintersManager() override;
 
   // Register the printing preferences with the |registry|.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -68,11 +69,11 @@
   // live on the same thread (UI) as this object.  OnPrinter* methods are
   // invoked inline so calling RegisterPrinter in response to OnPrinterAdded is
   // forbidden.
-  void AddObserver(PrintersManager::Observer* observer);
+  void AddObserver(SyncedPrintersManager::Observer* observer);
 
   // Remove |observer| so that it no longer receives notifications.  After the
   // completion of this method, the |observer| can be safely destroyed.
-  void RemoveObserver(PrintersManager::Observer* observer);
+  void RemoveObserver(SyncedPrintersManager::Observer* observer);
 
   // Returns a ModelTypeSyncBridge for the sync client.
   PrintersSyncBridge* GetSyncBridge();
@@ -104,9 +105,9 @@
 
   base::ObserverList<Observer> observers_;
 
-  DISALLOW_COPY_AND_ASSIGN(PrintersManager);
+  DISALLOW_COPY_AND_ASSIGN(SyncedPrintersManager);
 };
 
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_PRINTERS_MANAGER_H_
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_H_
diff --git a/chrome/browser/chromeos/printing/printers_manager_factory.cc b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
similarity index 66%
rename from chrome/browser/chromeos/printing/printers_manager_factory.cc
rename to chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
index 5f8de867..3c112102 100644
--- a/chrome/browser/chromeos/printing/printers_manager_factory.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 
 #include <memory>
 #include <utility>
@@ -22,36 +22,36 @@
 
 namespace {
 
-base::LazyInstance<PrintersManagerFactory>::DestructorAtExit
+base::LazyInstance<SyncedPrintersManagerFactory>::DestructorAtExit
     g_printers_manager = LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
 
 // static
-PrintersManager* PrintersManagerFactory::GetForBrowserContext(
+SyncedPrintersManager* SyncedPrintersManagerFactory::GetForBrowserContext(
     content::BrowserContext* context) {
-  return static_cast<PrintersManager*>(
+  return static_cast<SyncedPrintersManager*>(
       GetInstance()->GetServiceForBrowserContext(context, true));
 }
 
 // static
-PrintersManagerFactory* PrintersManagerFactory::GetInstance() {
+SyncedPrintersManagerFactory* SyncedPrintersManagerFactory::GetInstance() {
   return g_printers_manager.Pointer();
 }
 
-content::BrowserContext* PrintersManagerFactory::GetBrowserContextToUse(
+content::BrowserContext* SyncedPrintersManagerFactory::GetBrowserContextToUse(
     content::BrowserContext* context) const {
   return chrome::GetBrowserContextRedirectedInIncognito(context);
 }
 
-PrintersManagerFactory::PrintersManagerFactory()
+SyncedPrintersManagerFactory::SyncedPrintersManagerFactory()
     : BrowserContextKeyedServiceFactory(
-          "PrintersManager",
+          "SyncedPrintersManager",
           BrowserContextDependencyManager::GetInstance()) {}
 
-PrintersManagerFactory::~PrintersManagerFactory() {}
+SyncedPrintersManagerFactory::~SyncedPrintersManagerFactory() {}
 
-PrintersManager* PrintersManagerFactory::BuildServiceInstanceFor(
+SyncedPrintersManager* SyncedPrintersManagerFactory::BuildServiceInstanceFor(
     content::BrowserContext* browser_context) const {
   Profile* profile = Profile::FromBrowserContext(browser_context);
 
@@ -61,11 +61,10 @@
 
   std::unique_ptr<PrintersSyncBridge> sync_bridge =
       base::MakeUnique<PrintersSyncBridge>(
-          store_factory,
-          base::BindRepeating(
-              base::IgnoreResult(&base::debug::DumpWithoutCrashing)));
+          store_factory, base::BindRepeating(base::IgnoreResult(
+                             &base::debug::DumpWithoutCrashing)));
 
-  return new PrintersManager(profile, std::move(sync_bridge));
+  return new SyncedPrintersManager(profile, std::move(sync_bridge));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_factory.h b/chrome/browser/chromeos/printing/synced_printers_manager_factory.h
new file mode 100644
index 0000000..c9bd895
--- /dev/null
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_FACTORY_H_
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace chromeos {
+
+class SyncedPrintersManagerFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static SyncedPrintersManager* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static SyncedPrintersManagerFactory* GetInstance();
+
+ protected:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<SyncedPrintersManagerFactory>;
+
+  SyncedPrintersManagerFactory();
+  ~SyncedPrintersManagerFactory() override;
+
+  // BrowserContextKeyedServiceFactory implementation:
+  SyncedPrintersManager* BuildServiceInstanceFor(
+      content::BrowserContext* browser_context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncedPrintersManagerFactory);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/printers_manager_unittest.cc b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
similarity index 89%
rename from chrome/browser/chromeos/printing/printers_manager_unittest.cc
rename to chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
index d73860c..39e7c880 100644
--- a/chrome/browser/chromeos/printing/printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
 
 #include <memory>
 #include <utility>
@@ -13,8 +13,8 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/sync/model/fake_model_type_change_processor.h"
@@ -43,7 +43,7 @@
       } )json";
 
 // Helper class to record observed events.
-class LoggingObserver : public PrintersManager::Observer {
+class LoggingObserver : public SyncedPrintersManager::Observer {
  public:
   void OnPrinterAdded(const Printer& printer) override {
     last_added_ = printer;
@@ -84,9 +84,9 @@
 
 }  // namespace
 
-class PrintersManagerTest : public testing::Test {
+class SyncedPrintersManagerTest : public testing::Test {
  protected:
-  PrintersManagerTest()
+  SyncedPrintersManagerTest()
       : manager_(
             &profile_,
             base::MakeUnique<PrintersSyncBridge>(
@@ -103,10 +103,10 @@
   // Must outlive |manager_|.
   TestingProfile profile_;
 
-  PrintersManager manager_;
+  SyncedPrintersManager manager_;
 };
 
-TEST_F(PrintersManagerTest, AddPrinter) {
+TEST_F(SyncedPrintersManagerTest, AddPrinter) {
   LoggingObserver observer;
   manager_.AddObserver(&observer);
   manager_.RegisterPrinter(base::MakeUnique<Printer>(kPrinterId));
@@ -120,7 +120,7 @@
   EXPECT_FALSE(observer.UpdateCalled());
 }
 
-TEST_F(PrintersManagerTest, UpdatePrinterAssignsId) {
+TEST_F(SyncedPrintersManagerTest, UpdatePrinterAssignsId) {
   manager_.RegisterPrinter(base::MakeUnique<Printer>());
 
   auto printers = manager_.GetPrinters();
@@ -128,7 +128,7 @@
   EXPECT_FALSE(printers[0]->id().empty());
 }
 
-TEST_F(PrintersManagerTest, UpdatePrinter) {
+TEST_F(SyncedPrintersManagerTest, UpdatePrinter) {
   manager_.RegisterPrinter(base::MakeUnique<Printer>(kPrinterId));
   auto updated_printer = base::MakeUnique<Printer>(kPrinterId);
   updated_printer->set_uri(kUri);
@@ -147,7 +147,7 @@
   EXPECT_FALSE(observer.AddCalled());
 }
 
-TEST_F(PrintersManagerTest, RemovePrinter) {
+TEST_F(SyncedPrintersManagerTest, RemovePrinter) {
   manager_.RegisterPrinter(base::MakeUnique<Printer>("OtherUUID"));
   manager_.RegisterPrinter(base::MakeUnique<Printer>(kPrinterId));
   manager_.RegisterPrinter(base::MakeUnique<Printer>());
@@ -162,7 +162,7 @@
 
 // Tests for policy printers
 
-TEST_F(PrintersManagerTest, RecommendedPrinters) {
+TEST_F(SyncedPrintersManagerTest, RecommendedPrinters) {
   std::string first_printer =
       R"json({
       "display_name": "Color Laser",
@@ -195,7 +195,7 @@
   EXPECT_EQ(Printer::Source::SRC_POLICY, printers[1]->source());
 }
 
-TEST_F(PrintersManagerTest, GetRecommendedPrinter) {
+TEST_F(SyncedPrintersManagerTest, GetRecommendedPrinter) {
   std::string printer = kLexJson;
   auto value = base::MakeUnique<base::ListValue>();
   value->AppendString(printer);
@@ -215,18 +215,18 @@
   EXPECT_EQ(Printer::Source::SRC_POLICY, from_list.source());
 }
 
-TEST_F(PrintersManagerTest, PrinterNotInstalled) {
+TEST_F(SyncedPrintersManagerTest, PrinterNotInstalled) {
   Printer printer(kPrinterId, base::Time::FromInternalValue(1000));
   EXPECT_FALSE(manager_.IsConfigurationCurrent(printer));
 }
 
-TEST_F(PrintersManagerTest, PrinterIsInstalled) {
+TEST_F(SyncedPrintersManagerTest, PrinterIsInstalled) {
   Printer printer(kPrinterId, base::Time::FromInternalValue(1000));
   manager_.PrinterInstalled(printer);
   EXPECT_TRUE(manager_.IsConfigurationCurrent(printer));
 }
 
-TEST_F(PrintersManagerTest, UpdatedPrinterConfiguration) {
+TEST_F(SyncedPrintersManagerTest, UpdatedPrinterConfiguration) {
   Printer printer(kPrinterId, base::Time::FromInternalValue(1000));
   manager_.PrinterInstalled(printer);
 
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc
index 3be8d30..60723d7 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/usb_printer_detector.h"
 #include "chrome/browser/chromeos/printing/usb_printer_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -195,8 +195,8 @@
     // one for the one we generated automatically and skip the parts where we
     // try to automagically figure out the driver.
     std::unique_ptr<Printer> existing_printer_configuration =
-        PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter(
-            data->printer->id());
+        SyncedPrintersManagerFactory::GetForBrowserContext(profile_)
+            ->GetPrinter(data->printer->id());
     if (existing_printer_configuration != nullptr) {
       data->is_new = false;
       data->printer = std::move(existing_printer_configuration);
@@ -267,8 +267,8 @@
         // We aren't done with data->printer yet, so we have to copy it instead
         // of moving it.
         auto printer_copy = base::MakeUnique<Printer>(*data->printer);
-        PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
-            std::move(printer_copy));
+        SyncedPrintersManagerFactory::GetForBrowserContext(profile_)
+            ->RegisterPrinter(std::move(printer_copy));
       }
       // TODO(justincarlson): If the device was hotplugged, pop a timed
       // notification that says the printer is now available for printing.
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector_factory.cc b/chrome/browser/chromeos/printing/usb_printer_detector_factory.cc
index c8dbd12a..fc9b6647 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector_factory.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector_factory.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/chromeos/printing/usb_printer_detector_factory.h"
 
 #include "base/command_line.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/usb_printer_detector.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -39,7 +39,7 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
-  DependsOn(PrintersManagerFactory::GetInstance());
+  DependsOn(SyncedPrintersManagerFactory::GetInstance());
 }
 
 UsbPrinterDetectorFactory::~UsbPrinterDetectorFactory() {}
diff --git a/chrome/browser/chromeos/system_logs/command_line_log_source.h b/chrome/browser/chromeos/system_logs/command_line_log_source.h
index 16745ded..374ac69 100644
--- a/chrome/browser/chromeos/system_logs/command_line_log_source.h
+++ b/chrome/browser/chromeos/system_logs/command_line_log_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_COMMAND_LINE_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/dbus_log_source.h b/chrome/browser/chromeos/system_logs/dbus_log_source.h
index 2ffb2e2..a80140e 100644
--- a/chrome/browser/chromeos/system_logs/dbus_log_source.h
+++ b/chrome/browser/chromeos/system_logs/dbus_log_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_DBUS_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
index fa09b05..3b537ef 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
@@ -12,7 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/device_event_log_source.h b/chrome/browser/chromeos/system_logs/device_event_log_source.h
index 6337369..a3d8eb6 100644
--- a/chrome/browser/chromeos/system_logs/device_event_log_source.h
+++ b/chrome/browser/chromeos/system_logs/device_event_log_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_DEVICE_EVENT_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/lsb_release_log_source.h b/chrome/browser/chromeos/system_logs/lsb_release_log_source.h
index 4ec9e81..bfd39b4b 100644
--- a/chrome/browser/chromeos/system_logs/lsb_release_log_source.h
+++ b/chrome/browser/chromeos/system_logs/lsb_release_log_source.h
@@ -5,7 +5,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_LSB_RELEASE_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/single_debug_daemon_log_source.h b/chrome/browser/chromeos/system_logs/single_debug_daemon_log_source.h
index 97c4a480..b7d63111 100644
--- a/chrome/browser/chromeos/system_logs/single_debug_daemon_log_source.h
+++ b/chrome/browser/chromeos/system_logs/single_debug_daemon_log_source.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/chromeos/system_logs/single_log_file_log_source.h b/chrome/browser/chromeos/system_logs/single_log_file_log_source.h
index 861c1ed..e3ee9e3 100644
--- a/chrome/browser/chromeos/system_logs/single_log_file_log_source.h
+++ b/chrome/browser/chromeos/system_logs/single_log_file_log_source.h
@@ -11,8 +11,8 @@
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
 #include "components/feedback/anonymizer_tool.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace base {
 class Time;
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source.h b/chrome/browser/chromeos/system_logs/touch_log_source.h
index 08e4b6c..e2efaeee 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source.h
+++ b/chrome/browser/chromeos/system_logs/touch_log_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_TOUCH_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
index 2a814c5..ef781bd 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
@@ -8,8 +8,8 @@
 #include <memory>
 
 #include "chrome/browser/extensions/chrome_extension_function.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
 #include "chrome/common/extensions/api/feedback_private.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/extension_function.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service.h b/chrome/browser/extensions/api/feedback_private/feedback_service.h
index dc706ac..9e6dfa8 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service.h
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service.h
@@ -12,8 +12,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
 #include "components/feedback/feedback_data.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 class Profile;
 
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_access_manager.h b/chrome/browser/extensions/api/feedback_private/log_source_access_manager.h
index 95b0a2b..9d44b01 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_access_manager.h
+++ b/chrome/browser/extensions/api/feedback_private/log_source_access_manager.h
@@ -15,8 +15,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
 #include "chrome/common/extensions/api/feedback_private.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 #include "content/public/browser/browser_context.h"
 
 namespace extensions {
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_resource.h b/chrome/browser/extensions/api/feedback_private/log_source_resource.h
index 02c1a4e..e510360 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_resource.h
+++ b/chrome/browser/extensions/api/feedback_private/log_source_resource.h
@@ -10,7 +10,7 @@
 #include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "chrome/browser/extensions/api/feedback_private/log_source_access_manager.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 #include "extensions/browser/api/api_resource.h"
 #include "extensions/browser/api/api_resource_manager.h"
 
diff --git a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
index feb2943..5743d1a 100644
--- a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
+++ b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
@@ -8,8 +8,8 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
 #include "chrome/common/extensions/api/feedback_private.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 12b0727..e154c55 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -6354,6 +6354,35 @@
   EXPECT_FALSE(HasExternalInstallErrors(service_));
 }
 
+// Regression test for crbug.com/739142. Verifies that no UAF occurs when
+// ExternalInstallError needs to be deleted asynchronously.
+TEST_F(ExtensionServiceTest, InstallPromptAborted) {
+  FeatureSwitch::ScopedOverride prompt(
+      FeatureSwitch::prompt_for_external_extensions(), true);
+  InitializeEmptyExtensionService();
+
+  MockExternalProvider* reg_provider =
+      AddMockExternalProvider(Manifest::EXTERNAL_REGISTRY);
+
+  reg_provider->UpdateOrAddExtension(good_crx, "1.0.0.0",
+                                     data_dir().AppendASCII("good.crx"));
+  WaitForExternalExtensionInstalled();
+  EXPECT_EQ(
+      1u, service()->external_install_manager()->GetErrorsForTesting().size());
+  EXPECT_FALSE(service()->IsExtensionEnabled(good_crx));
+  EXPECT_TRUE(GetError(good_crx));
+
+  // Abort the extension install prompt. This should cause the
+  // ExternalInstallError to be deleted asynchronously.
+  GetError(good_crx)->OnInstallPromptDone(
+      ExtensionInstallPrompt::Result::ABORTED);
+  EXPECT_TRUE(GetError(good_crx));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(GetError(good_crx));
+
+  EXPECT_FALSE(HasExternalInstallErrors(service_));
+}
+
 TEST_F(ExtensionServiceTest, MultipleExternalInstallBubbleErrors) {
   FeatureSwitch::ScopedOverride prompt(
       FeatureSwitch::prompt_for_external_extensions(), true);
diff --git a/chrome/browser/extensions/external_install_manager.cc b/chrome/browser/extensions/external_install_manager.cc
index d1419d0..8eb7020f 100644
--- a/chrome/browser/extensions/external_install_manager.cc
+++ b/chrome/browser/extensions/external_install_manager.cc
@@ -114,10 +114,15 @@
     const std::string& extension_id) {
   auto iter = errors_.find(extension_id);
   if (iter != errors_.end()) {
+    // The |extension_id| may be owned by the ExternalInstallError, which is
+    // deleted subsequently. To avoid any UAFs, make a safe copy of
+    // |extension_id| now.
+    std::string extension_id_copy = extension_id;
+
     if (iter->second.get() == currently_visible_install_alert_)
       currently_visible_install_alert_ = nullptr;
     errors_.erase(iter);
-    unacknowledged_ids_.erase(extension_id);
+    unacknowledged_ids_.erase(extension_id_copy);
     UpdateExternalExtensionAlert();
   }
 }
diff --git a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
index 1149cf4..3bb9374 100644
--- a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
@@ -8,7 +8,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h"
 #include "chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/system_logs/command_line_log_source.h"
diff --git a/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
index 31df6ba1..30d49ef 100644
--- a/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h"
 #include "chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h"
 #include "chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/system_logs/command_line_log_source.h"
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h
index d947b407..5d33b844 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h
index 6339c5fb..5193bf5 100644
--- a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h
+++ b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/callback_forward.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 #include "components/upload_list/upload_list.h"
 
 namespace system_logs {
diff --git a/chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h b/chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h
index fc438b4..7edd90f 100644
--- a/chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h
+++ b/chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_MEMORY_DETAILS_LOG_SOURCE_H_
 
 #include "base/macros.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 684d2fb..1379b7be 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1180,6 +1180,8 @@
 const char kTLS13VariantDisabled[] = "Disabled";
 const char kTLS13VariantDraft[] = "Enabled (Draft)";
 const char kTLS13VariantExperiment[] = "Enabled (Experiment)";
+const char kTLS13VariantRecordTypeExperiment[] =
+    "Enabled (Record Type Experiment)";
 
 const char kTopDocumentIsolationName[] = "Top document isolation";
 const char kTopDocumentIsolationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0024836..a0ec35a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -707,6 +707,7 @@
 extern const char kTLS13VariantDisabled[];
 extern const char kTLS13VariantDraft[];
 extern const char kTLS13VariantExperiment[];
+extern const char kTLS13VariantRecordTypeExperiment[];
 
 extern const char kSuggestionsWithSubStringMatchName[];
 extern const char kSuggestionsWithSubStringMatchDescription[];
diff --git a/chrome/browser/media/router/media_router_metrics.cc b/chrome/browser/media/router/media_router_metrics.cc
index 491da23..7da3ae6 100644
--- a/chrome/browser/media/router/media_router_metrics.cc
+++ b/chrome/browser/media/router/media_router_metrics.cc
@@ -28,7 +28,9 @@
 const char MediaRouterMetrics::kHistogramMediaRouterCastingSource[] =
     "MediaRouter.Source.CastingSource";
 const char MediaRouterMetrics::kHistogramMediaRouterFileFormat[] =
-    "MediaRouter.Source.FileFormat";
+    "MediaRouter.Source.LocalFileFormat";
+const char MediaRouterMetrics::kHistogramMediaRouterFileSize[] =
+    "MediaRouter.Source.LocalFileSize";
 const char MediaRouterMetrics::kHistogramRouteCreationOutcome[] =
     "MediaRouter.Route.CreationOutcome";
 const char MediaRouterMetrics::kHistogramUiDialogPaint[] =
@@ -91,6 +93,10 @@
                             media::container_names::CONTAINER_MAX);
 }
 
+void MediaRouterMetrics::RecordMediaRouterFileSize(int64_t size) {
+  UMA_HISTOGRAM_MEMORY_LARGE_MB(kHistogramMediaRouterFileSize, size);
+}
+
 void MediaRouterMetrics::RecordDialDeviceCounts(size_t available_device_count,
                                                 size_t known_device_count) {
   if (clock_->Now() - device_count_metrics_record_time_ <
diff --git a/chrome/browser/media/router/media_router_metrics.h b/chrome/browser/media/router/media_router_metrics.h
index dd8dc022..f94ff203 100644
--- a/chrome/browser/media/router/media_router_metrics.h
+++ b/chrome/browser/media/router/media_router_metrics.h
@@ -68,6 +68,7 @@
   static const char kHistogramIconClickLocation[];
   static const char kHistogramMediaRouterCastingSource[];
   static const char kHistogramMediaRouterFileFormat[];
+  static const char kHistogramMediaRouterFileSize[];
   static const char kHistogramRouteCreationOutcome[];
   static const char kHistogramUiDialogPaint[];
   static const char kHistogramUiDialogLoadedWithData[];
@@ -103,6 +104,9 @@
   static void RecordMediaRouterFileFormat(
       media::container_names::MediaContainerName format);
 
+  // Records the size of a cast file.
+  static void RecordMediaRouterFileSize(int64_t size);
+
   // Records device counts.
   // TODO(zhaobin): Move device count specific metrics and state into its own
   // class eventually.
diff --git a/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc b/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
index 6a22aa6..5c8682c 100644
--- a/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
+++ b/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
@@ -164,17 +164,3 @@
 TEST_F(MediaStreamDevicePermissionContextTests, TestCameraSecureQueryingUrl) {
   TestSecureQueryingUrl(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
 }
-
-// An embedding origin of about:blank should not be blocked. crbug.com/740540.
-TEST_F(MediaStreamDevicePermissionContextTests, TestAboutBlankNotBlocked) {
-  TestPermissionContext permission_context(
-      profile(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
-  GURL secure_url("https://www.example.com");
-
-  EXPECT_EQ(
-      CONTENT_SETTING_ASK,
-      permission_context
-          .GetPermissionStatus(nullptr /* render_frame_host */, secure_url,
-                               GURL("about:blank").GetOrigin())
-          .content_setting);
-}
diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc
index d4dfb8c..8210831 100644
--- a/chrome/browser/permissions/permission_context_base.cc
+++ b/chrome/browser/permissions/permission_context_base.cc
@@ -230,20 +230,21 @@
   }
 
   if (IsRestrictedToSecureOrigins()) {
-    // TODO(raymes): The secure origin check here in the browser should match
-    // what we do in blink (i.e. what is described in the secure context spec).
-    // Right now, we can't even check IsOriginSecure(embedding_origin) because
-    // the |embedding_origin| is obtained from the WebContents which does match
-    // the origin of the document in blink in all cases. For example, an
-    // about:blank URL may be a secure context in blink, but it is not treated
-    // as such in the browser at present. The |requesting_origin| is passed from
-    // blink and so is accurate under normal circumstances but may be forged by
-    // a compromised renderer so even this check below is not particularly
-    // secure...
     if (!content::IsOriginSecure(requesting_origin)) {
       return PermissionResult(CONTENT_SETTING_BLOCK,
                               PermissionStatusSource::UNSPECIFIED);
     }
+
+    // TODO(raymes): We should check the entire chain of embedders here whenever
+    // possible as this corresponds to the requirements of the secure contexts
+    // spec and matches what is implemented in blink. Right now we just check
+    // the top level and requesting origins. Note: chrome-extension:// origins
+    // are currently exempt from checking the embedder chain. crbug.com/530507.
+    if (!requesting_origin.SchemeIs(extensions::kExtensionScheme) &&
+        !content::IsOriginSecure(embedding_origin)) {
+      return PermissionResult(CONTENT_SETTING_BLOCK,
+                              PermissionStatusSource::UNSPECIFIED);
+    }
   }
 
   // Check whether the feature is enabled for the frame by feature policy. We
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 01c8299a..222a42a 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -195,6 +195,7 @@
 #include "chrome/browser/chromeos/extensions/echo_private_api.h"
 #include "chrome/browser/chromeos/file_system_provider/registry.h"
 #include "chrome/browser/chromeos/first_run/first_run.h"
+#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_mode_detector.h"
 #include "chrome/browser/chromeos/login/quick_unlock/fingerprint_storage.h"
 #include "chrome/browser/chromeos/login/quick_unlock/pin_storage.h"
@@ -217,7 +218,7 @@
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
 #include "chrome/browser/chromeos/power/power_prefs.h"
 #include "chrome/browser/chromeos/preferences.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
 #include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
 #include "chrome/browser/chromeos/settings/device_settings_cache.h"
@@ -591,7 +592,7 @@
   chromeos::quick_unlock::FingerprintStorage::RegisterProfilePrefs(registry);
   chromeos::quick_unlock::PinStorage::RegisterProfilePrefs(registry);
   chromeos::Preferences::RegisterProfilePrefs(registry);
-  chromeos::PrintersManager::RegisterProfilePrefs(registry);
+  chromeos::SyncedPrintersManager::RegisterProfilePrefs(registry);
   chromeos::quick_unlock::RegisterProfilePrefs(registry);
   chromeos::SAMLOfflineSigninLimiter::RegisterProfilePrefs(registry);
   chromeos::ServicesCustomizationDocument::RegisterProfilePrefs(registry);
@@ -599,6 +600,7 @@
   chromeos::UserImageSyncObserver::RegisterProfilePrefs(registry);
   extensions::EPKPChallengeUserKey::RegisterProfilePrefs(registry);
   flags_ui::PrefServiceFlagsStorage::RegisterProfilePrefs(registry);
+  lock_screen_apps::StateController::RegisterProfilePrefs(registry);
   policy::DeviceStatusCollector::RegisterProfilePrefs(registry);
   ::onc::RegisterProfilePrefs(registry);
 #endif
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 1c940336..4288055 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -120,7 +120,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/usb_printer_detector_factory.h"
 #include "chrome/browser/chromeos/tether/tether_service_factory.h"
 #include "chrome/browser/extensions/api/platform_keys/verify_trust_api.h"
@@ -233,7 +233,7 @@
 #if defined(OS_CHROMEOS)
   chromeos::UsbPrinterDetectorFactory::GetInstance();
   chromeos::CupsPrintJobManagerFactory::GetInstance();
-  chromeos::PrintersManagerFactory::GetInstance();
+  chromeos::SyncedPrintersManagerFactory::GetInstance();
   TetherServiceFactory::GetInstance();
   extensions::VerifyTrustAPI::GetFactoryInstance();
 #endif
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 1b84c5f..e600164 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/resource_coordinator/resource_coordinator_web_contents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_manager_grc_tab_signal_observer.h"
 #include "chrome/browser/resource_coordinator/tab_manager_observer.h"
+#include "chrome/browser/resource_coordinator/tab_manager_stats_collector.h"
 #include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/ui/browser.h"
@@ -155,6 +156,7 @@
   if (resource_coordinator::IsResourceCoordinatorEnabled()) {
     grc_tab_signal_observer_.reset(new GRCTabSignalObserver());
   }
+  tab_manager_stats_collector_.reset(new TabManagerStatsCollector(this));
 }
 
 TabManager::~TabManager() {
@@ -825,6 +827,11 @@
                                   content::WebContents* new_contents,
                                   int index,
                                   int reason) {
+  // An active tab is not purged.
+  // Calling GetWebContentsData() early ensures that the WebContentsData is
+  // created for |new_contents|, which |tab_manager_stats_collector_| expects.
+  GetWebContentsData(new_contents)->set_is_purged(false);
+
   // If |old_contents| is set, that tab has switched from being active to
   // inactive, so record the time of that transition.
   if (old_contents) {
@@ -835,12 +842,9 @@
             GetTimeToPurge(min_time_to_purge_, max_time_to_purge_));
     // Only record switch-to-tab metrics when a switch happens, i.e.
     // |old_contents| is set.
-    RecordSwitchToTab(new_contents);
+    tab_manager_stats_collector_->RecordSwitchToTab(new_contents);
   }
 
-  // An active tab is not purged.
-  GetWebContentsData(new_contents)->set_is_purged(false);
-
   // Reload |web_contents| if it is in an active browser and discarded.
   if (IsActiveWebContentsInActiveBrowser(new_contents)) {
     ReloadWebContentsIfDiscarded(new_contents,
@@ -999,14 +1003,6 @@
   return browser_info_list;
 }
 
-void TabManager::RecordSwitchToTab(content::WebContents* contents) const {
-  if (is_session_restore_loading_tabs_) {
-    UMA_HISTOGRAM_ENUMERATION("TabManager.SessionRestore.SwitchToTab",
-                              GetWebContentsData(contents)->tab_loading_state(),
-                              TAB_LOADING_STATE_MAX);
-  }
-}
-
 content::NavigationThrottle::ThrottleCheckResult
 TabManager::MaybeThrottleNavigation(
     content::NavigationHandle* navigation_handle) {
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index aa5deb5f..83dcd12 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -47,6 +47,7 @@
 #if defined(OS_CHROMEOS)
 class TabManagerDelegate;
 #endif
+class TabManagerStatsCollector;
 
 // The TabManager periodically updates (see
 // |kAdjustmentIntervalSeconds| in the source) the status of renderers
@@ -209,13 +210,14 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, FastShutdownSingleTabProcess);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest,
                            GetUnsortedTabStatsIsInVisibleWindow);
-  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, HistogramsSessionRestoreSwitchToTab);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DiscardTabWithNonVisibleTabs);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, MaybeThrottleNavigation);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnDidFinishNavigation);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnDidStopLoading);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnWebContentsDestroyed);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnDelayedTabSelected);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerStatsCollectorTest,
+                           HistogramsSessionRestoreSwitchToTab);
 
   // Information about a Browser.
   struct BrowserInfo {
@@ -367,10 +369,6 @@
   void OnSessionRestoreStartedLoadingTabs();
   void OnSessionRestoreFinishedLoadingTabs();
 
-  // Records UMA histograms for the tab state when switching to a different tab
-  // during session restore.
-  void RecordSwitchToTab(content::WebContents* contents) const;
-
   // Returns true if the navigation should be delayed.
   bool ShouldDelayNavigation(
       content::NavigationHandle* navigation_handle) const;
@@ -476,6 +474,10 @@
   // GRC tab signal observer, receives tab scoped signal from GRC.
   std::unique_ptr<GRCTabSignalObserver> grc_tab_signal_observer_;
 
+  // Records UMAs for tab and system-related events and properties during
+  // session restore.
+  std::unique_ptr<TabManagerStatsCollector> tab_manager_stats_collector_;
+
   // Weak pointer factory used for posting delayed tasks.
   base::WeakPtrFactory<TabManager> weak_ptr_factory_;
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc b/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc
new file mode 100644
index 0000000..88e9953
--- /dev/null
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc
@@ -0,0 +1,28 @@
+// 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/resource_coordinator/tab_manager_stats_collector.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
+
+namespace resource_coordinator {
+
+TabManagerStatsCollector::TabManagerStatsCollector(TabManager* tab_manager)
+    : tab_manager_(tab_manager) {}
+
+TabManagerStatsCollector::~TabManagerStatsCollector() = default;
+
+void TabManagerStatsCollector::RecordSwitchToTab(
+    content::WebContents* contents) const {
+  if (tab_manager_->IsSessionRestoreLoadingTabs()) {
+    auto* data = TabManager::WebContentsData::FromWebContents(contents);
+    DCHECK(data);
+    UMA_HISTOGRAM_ENUMERATION("TabManager.SessionRestore.SwitchToTab",
+                              data->tab_loading_state(), TAB_LOADING_STATE_MAX);
+  }
+}
+
+}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector.h b/chrome/browser/resource_coordinator/tab_manager_stats_collector.h
new file mode 100644
index 0000000..b619571e
--- /dev/null
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector.h
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_MANAGER_STATS_COLLECTOR_H_
+#define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_MANAGER_STATS_COLLECTOR_H_
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace resource_coordinator {
+
+class TabManager;
+
+// TabManagerStatsCollector records UMAs on behalf of TabManager for tab and
+// system-related events and properties during session restore.
+class TabManagerStatsCollector {
+ public:
+  explicit TabManagerStatsCollector(TabManager* tab_manager);
+  ~TabManagerStatsCollector();
+
+  // Records UMA histograms for the tab state when switching to a different tab
+  // during session restore.
+  void RecordSwitchToTab(content::WebContents* contents) const;
+
+ private:
+  TabManager* tab_manager_;
+};
+
+}  // namespace resource_coordinator
+
+#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_MANAGER_STATS_COLLECTOR_H_
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
new file mode 100644
index 0000000..87ebfae
--- /dev/null
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
@@ -0,0 +1,78 @@
+// 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/resource_coordinator/tab_manager_stats_collector.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/test/histogram_tester.h"
+#include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "content/test/test_web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::WebContents;
+
+namespace resource_coordinator {
+
+class TabManagerStatsCollectorTest : public ChromeRenderViewHostTestHarness {
+ public:
+  WebContents* CreateWebContents() {
+    return content::TestWebContents::Create(profile(), nullptr);
+  }
+};
+
+TEST_F(TabManagerStatsCollectorTest, HistogramsSessionRestoreSwitchToTab) {
+  const char kHistogramName[] = "TabManager.SessionRestore.SwitchToTab";
+
+  TabManager tab_manager;
+  std::unique_ptr<WebContents> tab(CreateWebContents());
+
+  auto* data = tab_manager.GetWebContentsData(tab.get());
+  auto* stats_collector = tab_manager.tab_manager_stats_collector_.get();
+
+  base::HistogramTester histograms;
+  histograms.ExpectTotalCount(kHistogramName, 0);
+
+  data->SetTabLoadingState(TAB_IS_LOADING);
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+
+  // Nothing should happen until we're in a session restore.
+  histograms.ExpectTotalCount(kHistogramName, 0);
+
+  tab_manager.OnSessionRestoreStartedLoadingTabs();
+
+  data->SetTabLoadingState(TAB_IS_NOT_LOADING);
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+  histograms.ExpectTotalCount(kHistogramName, 2);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
+
+  data->SetTabLoadingState(TAB_IS_LOADING);
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+
+  histograms.ExpectTotalCount(kHistogramName, 5);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADING, 3);
+
+  data->SetTabLoadingState(TAB_IS_LOADED);
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+  stats_collector->RecordSwitchToTab(tab.get());
+
+  histograms.ExpectTotalCount(kHistogramName, 9);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADING, 3);
+  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADED, 4);
+}
+
+}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index 49537e1..2c94819 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
-#include "base/test/histogram_tester.h"
 #include "base/test/mock_entropy_provider.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
@@ -682,59 +681,6 @@
   EXPECT_FALSE(tab_manager->IsSessionRestoreLoadingTabs());
 }
 
-TEST_F(TabManagerTest, HistogramsSessionRestoreSwitchToTab) {
-  const char kHistogramName[] = "TabManager.SessionRestore.SwitchToTab";
-
-  TabManager tab_manager;
-  TabStripDummyDelegate delegate;
-  TabStripModel tab_strip(&delegate, profile());
-  WebContents* tab = CreateWebContents();
-  tab_strip.AppendWebContents(tab, true);
-
-  auto* data = tab_manager.GetWebContentsData(tab);
-
-  base::HistogramTester histograms;
-  histograms.ExpectTotalCount(kHistogramName, 0);
-
-  data->SetTabLoadingState(TAB_IS_LOADING);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-
-  // Nothing should happen until we're in a session restore
-  histograms.ExpectTotalCount(kHistogramName, 0);
-
-  tab_manager.OnSessionRestoreStartedLoadingTabs();
-
-  data->SetTabLoadingState(TAB_IS_NOT_LOADING);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-  histograms.ExpectTotalCount(kHistogramName, 2);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
-
-  data->SetTabLoadingState(TAB_IS_LOADING);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-
-  histograms.ExpectTotalCount(kHistogramName, 5);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADING, 3);
-
-  data->SetTabLoadingState(TAB_IS_LOADED);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-  tab_manager.RecordSwitchToTab(tab);
-
-  histograms.ExpectTotalCount(kHistogramName, 9);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_NOT_LOADING, 2);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADING, 3);
-  histograms.ExpectBucketCount(kHistogramName, TAB_IS_LOADED, 4);
-
-  // Tabs with a committed URL must be closed explicitly to avoid DCHECK errors.
-  tab_strip.CloseAllTabs();
-}
-
 TEST_F(TabManagerTest, MaybeThrottleNavigation) {
   TabManager* tab_manager = g_browser_process->GetTabManager();
   MaybeThrottleNavigations(tab_manager);
diff --git a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
index 379b46b..361e1db9 100644
--- a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
+++ b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
@@ -141,7 +141,6 @@
   // Needed to access tab_data_.
   FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, CopyState);
   FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, TabLoadingState);
-  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, HistogramsSessionRestoreSwitchToTab);
 
   struct Data {
     Data();
diff --git a/chrome/browser/resources/md_extensions/pack_dialog.html b/chrome/browser/resources/md_extensions/pack_dialog.html
index b315a819..9ef2cd4 100644
--- a/chrome/browser/resources/md_extensions/pack_dialog.html
+++ b/chrome/browser/resources/md_extensions/pack_dialog.html
@@ -1,8 +1,9 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 
diff --git a/chrome/browser/resources/md_extensions/shortcut_input.html b/chrome/browser/resources/md_extensions/shortcut_input.html
index 44a916d8..3645469 100644
--- a/chrome/browser/resources/md_extensions/shortcut_input.html
+++ b/chrome/browser/resources/md_extensions/shortcut_input.html
@@ -1,8 +1,9 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://extensions/shortcut_util.html">
diff --git a/chrome/browser/resources/md_extensions/toolbar.html b/chrome/browser/resources/md_extensions/toolbar.html
index faa032e..b1f44efdf 100644
--- a/chrome/browser/resources/md_extensions/toolbar.html
+++ b/chrome/browser/resources/md_extensions/toolbar.html
@@ -1,6 +1,7 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
diff --git a/chrome/browser/resources/print_preview/data/app_state.js b/chrome/browser/resources/print_preview/data/app_state.js
index 6bc3976..ffad895 100644
--- a/chrome/browser/resources/print_preview/data/app_state.js
+++ b/chrome/browser/resources/print_preview/data/app_state.js
@@ -177,9 +177,8 @@
         return this.state_[field] ?
             print_preview.Margins.parse(this.state_[field]) :
             null;
-      } else {
-        return this.state_[field];
       }
+      return this.state_[field];
     },
 
     /**
diff --git a/chrome/browser/resources/print_preview/data/cloud_parsers.js b/chrome/browser/resources/print_preview/data/cloud_parsers.js
index 24ab1b0..25e16c1 100644
--- a/chrome/browser/resources/print_preview/data/cloud_parsers.js
+++ b/chrome/browser/resources/print_preview/data/cloud_parsers.js
@@ -108,11 +108,11 @@
     if (typeStr == CloudDestinationParser.CloudType_.ANDROID ||
         typeStr == CloudDestinationParser.CloudType_.IOS) {
       return print_preview.DestinationType.MOBILE;
-    } else if (typeStr == CloudDestinationParser.CloudType_.DOCS) {
-      return print_preview.DestinationType.GOOGLE_PROMOTED;
-    } else {
-      return print_preview.DestinationType.GOOGLE;
     }
+    if (typeStr == CloudDestinationParser.CloudType_.DOCS) {
+      return print_preview.DestinationType.GOOGLE_PROMOTED;
+    }
+    return print_preview.DestinationType.GOOGLE;
   };
 
   /** Namespace which contains a method to parse printer sharing invitation. */
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 0980850e..3ccbb1e 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -1341,16 +1341,15 @@
         this.destinations_.push(destination);
         this.destinationMap_[key] = destination;
         return true;
-      } else if (
-          existingDestination.connectionStatus ==
+      }
+      if (existingDestination.connectionStatus ==
               print_preview.DestinationConnectionStatus.UNKNOWN &&
           destination.connectionStatus !=
               print_preview.DestinationConnectionStatus.UNKNOWN) {
         existingDestination.connectionStatus = destination.connectionStatus;
         return true;
-      } else {
-        return false;
       }
+      return false;
     },
 
     /**
diff --git a/chrome/browser/resources/print_preview/data/measurement_system.js b/chrome/browser/resources/print_preview/data/measurement_system.js
index 323c195..01c6a55 100644
--- a/chrome/browser/resources/print_preview/data/measurement_system.js
+++ b/chrome/browser/resources/print_preview/data/measurement_system.js
@@ -91,12 +91,11 @@
     get unitSymbol() {
       if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) {
         return 'mm';
-      } else if (
-          this.unitType_ == print_preview.MeasurementSystemUnitType.IMPERIAL) {
-        return '"';
-      } else {
-        throw Error('Unit type not supported: ' + this.unitType_);
       }
+      if (this.unitType_ == print_preview.MeasurementSystemUnitType.IMPERIAL) {
+        return '"';
+      }
+      throw Error('Unit type not supported: ' + this.unitType_);
     },
 
     /**
@@ -141,9 +140,8 @@
     convertFromPoints: function(pts) {
       if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) {
         return pts / MeasurementSystem.PTS_PER_MM_;
-      } else {
-        return pts / MeasurementSystem.PTS_PER_INCH_;
       }
+      return pts / MeasurementSystem.PTS_PER_INCH_;
     },
 
     /**
@@ -153,9 +151,8 @@
     convertToPoints: function(localUnits) {
       if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) {
         return localUnits * MeasurementSystem.PTS_PER_MM_;
-      } else {
-        return localUnits * MeasurementSystem.PTS_PER_INCH_;
       }
+      return localUnits * MeasurementSystem.PTS_PER_INCH_;
     }
   };
 
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js b/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js
index ff31b24..753c5164 100644
--- a/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js
+++ b/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js
@@ -106,15 +106,13 @@
 
     /** @return {print_preview.ValueType} The value of the ticket item. */
     getValue: function() {
-      if (this.isCapabilityAvailable()) {
-        if (this.value_ == null) {
-          return this.getDefaultValueInternal();
-        } else {
-          return this.value_;
-        }
-      } else {
+      if (!this.isCapabilityAvailable()) {
         return this.getCapabilityNotAvailableValueInternal();
       }
+      if (this.value_ == null) {
+        return this.getDefaultValueInternal();
+      }
+      return this.value_;
     },
 
     /** @return {boolean} Whether the ticket item was modified by the user. */
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js
index 536c62d4..812305f 100644
--- a/chrome/browser/resources/print_preview/previewarea/preview_area.js
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -523,41 +523,42 @@
       if (!this.previewGenerator_)
         return;
       var previewRequest = this.previewGenerator_.requestPreview();
-      if (previewRequest.id > -1) {
-        cr.dispatchSimpleEvent(
-            this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS);
-        if (this.loadingTimeout_ == null) {
-          this.loadingTimeout_ = setTimeout(
-              this.showMessage_.bind(
-                  this, print_preview.PreviewAreaMessageId_.LOADING),
-              PreviewArea.LOADING_TIMEOUT_);
-        }
-        previewRequest.request.then(
-            /** @param {number} previewUid The unique id of the preview. */
-            function(previewUid) {
-              this.previewGenerator_.onPreviewGenerationDone(
-                  previewRequest.id, previewUid);
-            }.bind(this),
-            /**
-             * @param {*} type The type of print preview failure that
-             *     occurred.
-             */
-            function(type) {
-              if (/** @type{string} */ (type) == 'CANCELLED')
-                return;  // overriden by a new request, do nothing.
-              else if (/** @type{string} */ (type) == 'SETTINGS_INVALID') {
-                this.cancelTimeout();
-                this.showCustomMessage(
-                    loadTimeData.getString('invalidPrinterSettings'));
-                cr.dispatchSimpleEvent(
-                    this, PreviewArea.EventType.SETTINGS_INVALID);
-              } else {
-                this.onPreviewGenerationFail_();
-              }
-            }.bind(this));
-      } else {
+      if (previewRequest.id <= -1) {
         this.marginControlContainer_.showMarginControlsIfNeeded();
+        return;
       }
+
+      cr.dispatchSimpleEvent(
+          this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS);
+      if (this.loadingTimeout_ == null) {
+        this.loadingTimeout_ = setTimeout(
+            this.showMessage_.bind(
+                this, print_preview.PreviewAreaMessageId_.LOADING),
+            PreviewArea.LOADING_TIMEOUT_);
+      }
+      previewRequest.request.then(
+          /** @param {number} previewUid The unique id of the preview. */
+          function(previewUid) {
+            this.previewGenerator_.onPreviewGenerationDone(
+                previewRequest.id, previewUid);
+          }.bind(this),
+          /**
+           * @param {*} type The type of print preview failure that
+           *     occurred.
+           */
+          function(type) {
+            if (/** @type{string} */ (type) == 'CANCELLED')
+              return;  // overriden by a new request, do nothing.
+            if (/** @type{string} */ (type) == 'SETTINGS_INVALID') {
+              this.cancelTimeout();
+              this.showCustomMessage(
+                  loadTimeData.getString('invalidPrinterSettings'));
+              cr.dispatchSimpleEvent(
+                  this, PreviewArea.EventType.SETTINGS_INVALID);
+            } else {
+              this.onPreviewGenerationFail_();
+            }
+          }.bind(this));
     },
 
     /**
diff --git a/chrome/browser/resources/print_preview/settings/page_settings.js b/chrome/browser/resources/print_preview/settings/page_settings.js
index 4002ca61..f8f70ee 100644
--- a/chrome/browser/resources/print_preview/settings/page_settings.js
+++ b/chrome/browser/resources/print_preview/settings/page_settings.js
@@ -146,28 +146,28 @@
      * @private
      */
     setInvalidStateVisible_: function(validity) {
-      if (validity !== PageRangeStatus.NO_ERROR) {
-        var message;
-        if (validity === PageRangeStatus.LIMIT_ERROR) {
-          if (this.pageRangeTicketItem_.getDocumentNumPages()) {
-            message = loadTimeData.getStringF(
-                'pageRangeLimitInstructionWithValue',
-                this.pageRangeTicketItem_.getDocumentNumPages());
-          } else {
-            message = loadTimeData.getString('pageRangeLimitInstruction');
-          }
-        } else {
-          message = loadTimeData.getStringF(
-              'pageRangeSyntaxInstruction',
-              loadTimeData.getString('examplePageRangeText'));
-        }
-        this.customHintEl_.textContent = message;
-        this.customInput_.classList.add('invalid');
-        fadeInElement(this.customHintEl_);
-      } else {
+      if (validity === PageRangeStatus.NO_ERROR) {
         this.customInput_.classList.remove('invalid');
         fadeOutElement(this.customHintEl_);
+        return;
       }
+      var message;
+      if (validity === PageRangeStatus.LIMIT_ERROR) {
+        if (this.pageRangeTicketItem_.getDocumentNumPages()) {
+          message = loadTimeData.getStringF(
+              'pageRangeLimitInstructionWithValue',
+              this.pageRangeTicketItem_.getDocumentNumPages());
+        } else {
+          message = loadTimeData.getString('pageRangeLimitInstruction');
+        }
+      } else {
+        message = loadTimeData.getStringF(
+            'pageRangeSyntaxInstruction',
+            loadTimeData.getString('examplePageRangeText'));
+      }
+      this.customHintEl_.textContent = message;
+      this.customInput_.classList.add('invalid');
+      fadeInElement(this.customHintEl_);
     },
 
     /**
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
index c1456201..2b0ec22 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
@@ -1,5 +1,7 @@
-<link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html">
@@ -42,7 +44,7 @@
       #show-files-button {
         --paper-button-ink-color: white;
         /* Left-align the text of the button with the rest of the card's text */
-        -webkit-margin-start: calc(var(--settings-button-edge-spacing) * -1);
+        -webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1);
         color: var(--google-blue-700);
         text-transform: inherit;
       }
diff --git a/chrome/browser/resources/settings/controls/controlled_button.html b/chrome/browser/resources/settings/controls/controlled_button.html
index 5cc109a..e013479 100644
--- a/chrome/browser/resources/settings/controls/controlled_button.html
+++ b/chrome/browser/resources/settings/controls/controlled_button.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="pref_control_behavior.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../settings_shared_css.html">
@@ -13,8 +14,8 @@
     <style include="settings-shared">
       :host {
         --justify-margin: 8px;
-        -webkit-margin-end: calc(var(--settings-button-edge-spacing) * -1);
-        -webkit-margin-start: calc(var(--settings-button-edge-spacing) * -1);
+        -webkit-margin-end: calc(var(--cr-button-edge-spacing) * -1);
+        -webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1);
         align-items: center;
         display: flex;
       }
diff --git a/chrome/browser/resources/settings/people_page/change_picture.js b/chrome/browser/resources/settings/people_page/change_picture.js
index fe9f948..26c9ff6 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.js
+++ b/chrome/browser/resources/settings/people_page/change_picture.js
@@ -88,6 +88,7 @@
   currentRouteChanged: function(newRoute) {
     if (newRoute == settings.routes.CHANGE_PICTURE) {
       this.browserProxy_.initialize();
+      this.browserProxy_.requestSelectedImage();
       this.pictureList_.setFocus();
     } else {
       // Ensure we deactivate the camera when we navigate away.
diff --git a/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js b/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
index d6dc9b9..1d682e8 100644
--- a/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
@@ -61,6 +61,9 @@
      * expected.
      */
     chooseFile() {}
+
+    /** Requests the currently selected image. */
+    requestSelectedImage() {}
   }
 
   /**
@@ -96,6 +99,11 @@
     chooseFile() {
       chrome.send('chooseFile');
     }
+
+    /** @override */
+    requestSelectedImage() {
+      chrome.send('requestSelectedImage');
+    }
   }
 
   // The singleton instance_ is replaced with a test version of this wrapper
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index a04c9a79..808d616ba 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -367,16 +367,28 @@
     currentDialog_: String,
 
     /** @private {boolean} */
-    showDiscoveryDialog_: Boolean,
+    showDiscoveryDialog_: {
+      type: Boolean,
+      value: false,
+    },
 
     /** @private {boolean} */
-    showManuallyAddDialog_: Boolean,
+    showManuallyAddDialog_: {
+      type: Boolean,
+      value: false,
+    },
 
     /** @private {boolean} */
-    showConfiguringDialog_: Boolean,
+    showConfiguringDialog_: {
+      type: Boolean,
+      value: false,
+    },
 
     /** @private {boolean} */
-    showManufacturerDialog_: Boolean,
+    showManufacturerDialog_: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   listeners: {
@@ -525,6 +537,7 @@
     // "ADD PRINTER" button.
     if (!this.previousDialog_) {
       this.$$('add-printer-discovery-dialog').close();
+      this.newPrinter = getEmptyPrinter_();
       this.openManuallyAddPrinterDialog_();
     }
   },
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.html b/chrome/browser/resources/settings/printing_page/cups_printers.html
index c6ffedd..6a3b9fe 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.html
@@ -12,16 +12,17 @@
 <dom-module id="settings-cups-printers">
   <template>
     <style include="settings-shared action-link">
-      .settings-box .start {
-        color: var(--paper-grey-600);
-      }
-
       .settings-box .start .secondary {
         color: var(--paper-grey-800);
         font-size: 92.31%;  /* 12px / 13px */
       }
-      
+
       .settings-box .primary-button {
+        --paper-button-disabled: {
+          background: none;
+          color: black;
+          opacity: 0.26;
+        };
         -webkit-margin-end: 0;
         -webkit-margin-start: auto;
       }
@@ -55,14 +56,19 @@
     </style>
 
     <div class="settings-box first">
+      <div class="start">
+        <span>$i18n{cupsPrintersLearnMoreLabel}</span>
+        <a href="$i18n{printingCUPSPrintLearnMoreUrl}" target="_blank">
+          $i18n{learnMore}
+        </a>
+        <div class="secondary" hidden="[[canAddPrinter_]]">
+          $i18n{requireNetworkMessage}
+        </div>
+      </div>
       <paper-button class="primary-button" id="addPrinter"
-          on-tap="onAddPrinterTap_" hidden="[[!canAddPrinter_]]">
+          on-tap="onAddPrinterTap_" disabled="[[!canAddPrinter_]]">
         $i18n{addCupsPrinter}
       </paper-button>
-      <div class="start" hidden="[[canAddPrinter_]]">
-        <span>$i18n{addCupsPrinter}</span>
-        <div class="secondary">$i18n{requireNetworkMessage}</div>
-      </div>
     </div>
 
     <settings-cups-add-printer-dialog id="addPrinterDialog"
diff --git a/chrome/browser/resources/settings/printing_page/printing_page.html b/chrome/browser/resources/settings/printing_page/printing_page.html
index 48b76bb8..6f9f7516 100644
--- a/chrome/browser/resources/settings/printing_page/printing_page.html
+++ b/chrome/browser/resources/settings/printing_page/printing_page.html
@@ -31,14 +31,9 @@
           </div>
         </template>
 </if>
-        <div id="cloudPrinters" class="settings-box two-line"
+        <div id="cloudPrinters" class="settings-box"
             on-tap="onTapCloudPrinters_" actionable>
-          <div class="start">
-            $i18n{cloudPrintersTitle}
-            <div class="secondary" id="cloudPrintersSecondary">
-              $i18n{cloudPrintersTitleDescription}
-            </div>
-          </div>
+          <div class="start">$i18n{cloudPrintersTitle}</div>
           <button class="subpage-arrow" is="paper-icon-button-light"
               aria-label="$i18n{cloudPrintersTitle}"
               aria-describedby="cloudPrintersSecondary"></button>
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 0591b4d2..279c57df 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -1,3 +1,5 @@
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="settings_icons_css.html">
 <link rel="import" href="settings_vars_css.html">
@@ -5,7 +7,7 @@
 <!-- Common styles for Material Design settings. -->
 <dom-module id="settings-shared">
   <template>
-    <style include="settings-icons cr-shared-style">
+    <style include="settings-icons paper-button-style cr-shared-style">
       /* Prevent action-links from being selected to avoid accidental
        * selection when trying to click it. */
       a[is=action-link] {
@@ -38,25 +40,7 @@
         -webkit-margin-start: var(--cr-icon-ripple-margin);
       }
 
-      /* See notes in .primary-button.
-       * TODO(dschuyler): Remove unnecessary .secondary-button references. */
-      paper-button {
-        --paper-button: {
-          -webkit-padding-end: var(--settings-button-edge-spacing);
-          -webkit-padding-start: var(--settings-button-edge-spacing);
-          color: var(--paper-grey-600);
-          font-weight: 500;
-          min-width: 1em;  /* A tighter fit than 5.14em for short buttons. */
-          text-decoration: none;
-        };
-        --paper-button-flat-keyboard-focus: {
-          background: rgba(0, 0, 0, .12);
-        };
-        flex-shrink: 0;
-        height: 36px;
-        margin: 0;
-      }
-
+      /* For "Advanced" toggle button. */
       paper-button[toggles][active] {
         background-color: var(--paper-grey-300);
       }
@@ -64,7 +48,7 @@
       /* If a button is at the end of the row, shift it to overlap the end of
        * the row. */
       .settings-box paper-button:last-of-type {
-        -webkit-margin-end: calc(var(--settings-button-edge-spacing) * -1);
+        -webkit-margin-end: calc(var(--cr-button-edge-spacing) * -1);
       }
 
       /* Special case for buttons inside of toggle-buttons. */
@@ -79,7 +63,7 @@
 
       /* Adjust the margin between the separator and the first button. */
       .settings-box .separator + paper-button {
-        -webkit-margin-start: calc(var(--settings-button-edge-spacing) * -1);
+        -webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1);
       }
 
       /* There are two settings button styles, .primary-button and normal
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html
index 9de80593..153aa5e 100644
--- a/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -13,7 +13,6 @@
      * UX design (bettes@). */
 
     --settings-actionable: var(--cr-actionable);
-    --settings-button-edge-spacing: 12px;
 
     --settings-box-row-padding: 20px;
     --settings-box-row-indent: calc(
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index ac80a07..09f9903 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -40,6 +40,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/browser/threat_details.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/web_ui/constants.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "components/safe_browsing_db/util.h"
@@ -802,6 +803,31 @@
   EXPECT_TRUE(YesInterstitial());
 }
 
+IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, HardcodedUrls) {
+  const GURL urls[] = {GURL(kChromeUISafeBrowsingMatchMalwareUrl),
+                       GURL(kChromeUISafeBrowsingMatchPhishingUrl),
+                       GURL(kChromeUISafeBrowsingMatchUnwantedUrl)};
+
+  for (const GURL& url : urls) {
+    ui_test_utils::NavigateToURL(browser(), url);
+    EXPECT_TRUE(WaitForReady(browser()));
+
+    EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
+    EXPECT_EQ(HIDDEN, GetVisibility("details"));
+    EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
+    EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
+    EXPECT_TRUE(Click("details-button"));
+    EXPECT_EQ(VISIBLE, GetVisibility("details"));
+    EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
+    EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
+    EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
+
+    AssertNoInterstitial(false);          // Assert the interstitial is gone
+    EXPECT_EQ(GURL(url::kAboutBlankURL),  // Back to "about:blank"
+              browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+  }
+}
+
 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, DontProceed) {
   SetupWarningAndNavigate(browser());
 
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 8dbeb11..75e3f7a5 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -114,9 +114,9 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/arc/arc_util.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_package_sync_data_type_controller.h"
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service.h"
 #include "components/sync_wifi/wifi_credential_syncable_service.h"
@@ -507,7 +507,8 @@
           web_data_service_.get());
 #if defined(OS_CHROMEOS)
     case syncer::PRINTERS:
-      return chromeos::PrintersManagerFactory::GetForBrowserContext(profile_)
+      return chromeos::SyncedPrintersManagerFactory::GetForBrowserContext(
+                 profile_)
           ->GetSyncBridge()
           ->AsWeakPtr();
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index f066d65c..3ce1976 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -68,7 +68,7 @@
 #endif  // !defined(OS_ANDROID)
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "components/sync_wifi/wifi_credential_syncable_service_factory.h"
 #endif  // defined(OS_CHROMEOS)
 
@@ -159,7 +159,7 @@
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 #if defined(OS_CHROMEOS)
-  DependsOn(chromeos::PrintersManagerFactory::GetInstance());
+  DependsOn(chromeos::SyncedPrintersManagerFactory::GetInstance());
   DependsOn(sync_wifi::WifiCredentialSyncableServiceFactory::GetInstance());
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/sync/test/integration/printers_helper.cc b/chrome/browser/sync/test/integration/printers_helper.cc
index 540b37f..d1d8a196 100644
--- a/chrome/browser/sync/test/integration/printers_helper.cc
+++ b/chrome/browser/sync/test/integration/printers_helper.cc
@@ -14,8 +14,8 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "content/public/test/test_utils.h"
@@ -67,9 +67,10 @@
   return base::StringPrintf("printer%d", index);
 }
 
-chromeos::PrintersManager* GetPrinterStore(content::BrowserContext* context) {
-  chromeos::PrintersManager* manager =
-      chromeos::PrintersManagerFactory::GetForBrowserContext(context);
+chromeos::SyncedPrintersManager* GetPrinterStore(
+    content::BrowserContext* context) {
+  chromeos::SyncedPrintersManager* manager =
+      chromeos::SyncedPrintersManagerFactory::GetForBrowserContext(context);
 
   // TODO(sync): crbug.com/709094: Remove all of this once the bug is fixed.
   // Must wait for ModelTypeStore initialization. It is fairly difficult to get
@@ -85,17 +86,17 @@
 
 }  // namespace
 
-void AddPrinter(chromeos::PrintersManager* manager,
+void AddPrinter(chromeos::SyncedPrintersManager* manager,
                 const chromeos::Printer& printer) {
   manager->RegisterPrinter(base::MakeUnique<chromeos::Printer>(printer));
 }
 
-void RemovePrinter(chromeos::PrintersManager* manager, int index) {
+void RemovePrinter(chromeos::SyncedPrintersManager* manager, int index) {
   chromeos::Printer testPrinter(CreateTestPrinter(index));
   manager->RemovePrinter(testPrinter.id());
 }
 
-bool EditPrinterDescription(chromeos::PrintersManager* manager,
+bool EditPrinterDescription(chromeos::SyncedPrintersManager* manager,
                             int index,
                             const std::string& description) {
   PrinterList printers = manager->GetPrinters();
@@ -123,15 +124,15 @@
   return printer;
 }
 
-chromeos::PrintersManager* GetVerifierPrinterStore() {
-  chromeos::PrintersManager* manager =
+chromeos::SyncedPrintersManager* GetVerifierPrinterStore() {
+  chromeos::SyncedPrintersManager* manager =
       GetPrinterStore(sync_datatype_helper::test()->verifier());
 
   return manager;
 }
 
-chromeos::PrintersManager* GetPrinterStore(int index) {
-  chromeos::PrintersManager* manager =
+chromeos::SyncedPrintersManager* GetPrinterStore(int index) {
+  chromeos::SyncedPrintersManager* manager =
       GetPrinterStore(sync_datatype_helper::test()->GetProfile(index));
 
   return manager;
diff --git a/chrome/browser/sync/test/integration/printers_helper.h b/chrome/browser/sync/test/integration/printers_helper.h
index a88265c..c68d16e 100644
--- a/chrome/browser/sync/test/integration/printers_helper.h
+++ b/chrome/browser/sync/test/integration/printers_helper.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
 #include "chrome/browser/sync/test/integration/await_match_status_change_checker.h"
 #include "chromeos/printing/printer_configuration.h"
 
@@ -22,23 +22,23 @@
 chromeos::Printer CreateTestPrinter(int index);
 
 // Add printer to the supplied store.
-void AddPrinter(chromeos::PrintersManager* manager,
+void AddPrinter(chromeos::SyncedPrintersManager* manager,
                 const chromeos::Printer& printer);
 
 // Remove printer |index| from the |manager|.
-void RemovePrinter(chromeos::PrintersManager* manager, int index);
+void RemovePrinter(chromeos::SyncedPrintersManager* manager, int index);
 
 // Change the description of the printer at |index| with |description|.  Returns
 // false if the printer is not tracked by the manager.
-bool EditPrinterDescription(chromeos::PrintersManager* manager,
+bool EditPrinterDescription(chromeos::SyncedPrintersManager* manager,
                             int index,
                             const std::string& description);
 
 // Returns the verifier store.
-chromeos::PrintersManager* GetVerifierPrinterStore();
+chromeos::SyncedPrintersManager* GetVerifierPrinterStore();
 
 // Returns printer store at |index|.
-chromeos::PrintersManager* GetPrinterStore(int index);
+chromeos::SyncedPrintersManager* GetPrinterStore(int index);
 
 // Returns the number of printers in the verifier store.
 int GetVerifierPrinterCount();
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
index fdc9a99..eb7e503 100644
--- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/common/autofill_pref_names.h"
 #include "components/user_prefs/user_prefs.h"
@@ -91,9 +92,11 @@
                                       base::Bind(&SaveCardCallback));
   }
 
-  void ShowLocalBubble() {
-    controller()->ShowBubbleForLocalSave(CreditCard(),
-                                         base::Bind(&SaveCardCallback));
+  void ShowLocalBubble(CreditCard* card = nullptr) {
+    controller()->ShowBubbleForLocalSave(
+        card ? CreditCard(*card)
+             : autofill::test::GetCreditCard(),  // Visa by default
+        base::Bind(&SaveCardCallback));
   }
 
   void ShowUploadBubble(bool should_cvc_be_requested = false) {
@@ -160,7 +163,7 @@
 
 TEST_F(SaveCardBubbleControllerImplTest,
        PropagateShouldRequestCvcFromUserWhenTrue) {
-  ShowUploadBubble(true /* should_cvc_be_requested */);
+  ShowUploadBubble(/*should_cvc_be_requested=*/true);
   EXPECT_TRUE(controller()->ShouldRequestCvcFromUser());
 }
 
@@ -203,7 +206,7 @@
 TEST_F(SaveCardBubbleControllerImplTest,
        Metrics_Upload_FirstShow_ShowBubble_RequestCvc) {
   base::HistogramTester histogram_tester;
-  ShowUploadBubble(true /* should_cvc_be_requested */);
+  ShowUploadBubble(/*should_cvc_be_requested=*/true);
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples(
@@ -212,7 +215,8 @@
                   Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
 }
 
-TEST_F(SaveCardBubbleControllerImplTest, Metrics_Upload_Reshows_ShowBubble) {
+TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_Reshows_ShowBubble_NotRequestCvc) {
   ShowUploadBubble();
 
   base::HistogramTester histogram_tester;
@@ -225,6 +229,20 @@
                   Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
 }
 
+TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_Reshows_ShowBubble_RequestCvc) {
+  ShowUploadBubble(/*should_cvc_be_requested=*/true);
+
+  base::HistogramTester histogram_tester;
+  CloseAndReshowBubble();
+
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.SaveCreditCardPrompt.Upload.Reshows"),
+      ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1),
+                  Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
+}
+
 TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_FirstShow_SaveButton) {
   ShowLocalBubble();
 
@@ -337,8 +355,17 @@
   ShowLocalBubble();
 
   base::HistogramTester histogram_tester;
-  // Fake-navigate after bubble has been visible for a long time.
-  controller()->set_elapsed(base::TimeDelta::FromMinutes(1));
+  // The bubble should still stick around for up to kSurviveNavigationSeconds
+  // (5) seconds regardless of navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(3));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.SaveCreditCardPrompt.Local.FirstShow", 0);
+
+  // Wait 3 more seconds (6 total); bubble should go away on next navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(6));
 
   controller()->SimulateNavigation();
 
@@ -353,8 +380,18 @@
   CloseAndReshowBubble();
 
   base::HistogramTester histogram_tester;
-  // Fake-navigate after bubble has been visible for a long time.
-  controller()->set_elapsed(base::TimeDelta::FromMinutes(1));
+  // The bubble should still stick around for up to kSurviveNavigationSeconds
+  // (5) seconds regardless of navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(3));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.SaveCreditCardPrompt.Local.Reshows", 0);
+
+  // Wait 3 more seconds (6 total); bubble should go away on next navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(6));
+
   controller()->SimulateNavigation();
 
   histogram_tester.ExpectUniqueSample(
@@ -363,6 +400,55 @@
 }
 
 TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_FirstShow_NavigateWhileShowing) {
+  ShowUploadBubble();
+
+  base::HistogramTester histogram_tester;
+  // The bubble should still stick around for up to kSurviveNavigationSeconds
+  // (5) seconds regardless of navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(3));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.SaveCreditCardPrompt.Upload.FirstShow", 0);
+
+  // Wait 3 more seconds (6 total); bubble should go away on next navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(6));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Upload.FirstShow",
+      AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1);
+}
+
+TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_Reshows_NavigateWhileShowing) {
+  ShowUploadBubble();
+  CloseAndReshowBubble();
+
+  base::HistogramTester histogram_tester;
+  // The bubble should still stick around for up to kSurviveNavigationSeconds
+  // (5) seconds regardless of navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(3));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectTotalCount(
+      "Autofill.SaveCreditCardPrompt.Upload.Reshows", 0);
+
+  // Wait 3 more seconds (6 total); bubble should go away on next navigation.
+  controller()->set_elapsed(base::TimeDelta::FromSeconds(6));
+
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Upload.Reshows",
+      AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1);
+}
+
+TEST_F(SaveCardBubbleControllerImplTest,
        Metrics_Local_FirstShow_NavigateWhileHidden) {
   ShowLocalBubble();
 
@@ -393,6 +479,60 @@
       AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN, 1);
 }
 
+TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_FirstShow_NavigateWhileHidden) {
+  ShowUploadBubble();
+
+  base::HistogramTester histogram_tester;
+  controller()->OnBubbleClosed();
+  // Fake-navigate after bubble has been visible for a long time.
+  controller()->set_elapsed(base::TimeDelta::FromMinutes(1));
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Upload.FirstShow",
+      AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN, 1);
+}
+
+TEST_F(SaveCardBubbleControllerImplTest,
+       Metrics_Upload_Reshows_NavigateWhileHidden) {
+  ShowUploadBubble();
+  CloseAndReshowBubble();
+
+  base::HistogramTester histogram_tester;
+  controller()->OnBubbleClosed();
+  // Fake-navigate after bubble has been visible for a long time.
+  controller()->set_elapsed(base::TimeDelta::FromMinutes(1));
+  controller()->SimulateNavigation();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Upload.Reshows",
+      AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN, 1);
+}
+
+TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_FirstShow_LearnMore) {
+  ShowLocalBubble();
+
+  base::HistogramTester histogram_tester;
+  controller()->OnLearnMoreClicked();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Local.FirstShow",
+      AutofillMetrics::SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE, 1);
+}
+
+TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_Reshows_LearnMore) {
+  ShowLocalBubble();
+  CloseAndReshowBubble();
+
+  base::HistogramTester histogram_tester;
+  controller()->OnLearnMoreClicked();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPrompt.Local.Reshows",
+      AutofillMetrics::SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE, 1);
+}
+
 TEST_F(SaveCardBubbleControllerImplTest, Metrics_Upload_FirstShow_LearnMore) {
   ShowUploadBubble();
 
@@ -523,4 +663,27 @@
           .empty());
 }
 
+TEST_F(SaveCardBubbleControllerImplTest, TestInputCvcIsValid) {
+  // Note: InputCvcIsValid(~) handles string trimming.
+  // 3-digit CVC:
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("text")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("1")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("12")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("12 ")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("1234")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("12345")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("1x3")));
+  EXPECT_TRUE(controller()->InputCvcIsValid(base::ASCIIToUTF16("123")));
+  EXPECT_TRUE(controller()->InputCvcIsValid(base::ASCIIToUTF16("123 ")));
+  EXPECT_TRUE(controller()->InputCvcIsValid(base::ASCIIToUTF16(" 123")));
+  EXPECT_TRUE(controller()->InputCvcIsValid(base::ASCIIToUTF16("999")));
+  // 4-digit CVC:
+  CreditCard amex = autofill::test::GetCreditCard2();  // Amex
+  ShowLocalBubble(&amex);
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("123")));
+  EXPECT_TRUE(controller()->InputCvcIsValid(base::ASCIIToUTF16("1234")));
+  EXPECT_FALSE(controller()->InputCvcIsValid(base::ASCIIToUTF16("12345")));
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index ec95ae6..79ac1d8 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -1181,7 +1181,7 @@
   EnsureFindBoxOpen();
 
   EXPECT_EQ(ASCIIToUTF16("page"), GetFindBarText());
-  EXPECT_EQ(ASCIIToUTF16("1 of 1"), GetMatchCountText());
+  EXPECT_EQ(ASCIIToUTF16("1/1"), GetMatchCountText());
 
   // Close the Find box.
   browser()->GetFindBarController()->EndFindSession(
@@ -1194,7 +1194,7 @@
   // After the Find box has been reopened, it should have been prepopulated with
   // the word "page" again.
   EXPECT_EQ(ASCIIToUTF16("page"), GetFindBarText());
-  EXPECT_EQ(ASCIIToUTF16("1 of 1"), GetMatchCountText());
+  EXPECT_EQ(ASCIIToUTF16("1/1"), GetMatchCountText());
 }
 
 // This tests that whenever you open Find in a new tab it should prepopulate
@@ -1211,7 +1211,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(1, FindInPageASCII(web_contents_1, "page",
                                kFwd, kIgnoreCase, &ordinal));
-  EXPECT_EQ(ASCIIToUTF16("1 of 1"), GetMatchCountText());
+  EXPECT_EQ(ASCIIToUTF16("1/1"), GetMatchCountText());
 
   // Now create a second tab and load the same page.
   chrome::AddSelectedTabWithURL(browser(), url, ui::PAGE_TRANSITION_TYPED);
@@ -1474,14 +1474,14 @@
   GURL url = GetURL(kSimple);
   ui_test_utils::NavigateToURL(browser(), url);
 
-  // Change the match count on the first tab to "1 of 1".
+  // Change the match count on the first tab to "1/1".
   int ordinal = 0;
   WebContents* web_contents_1 =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_EQ(1, FindInPageASCII(web_contents_1, "page",
                                kFwd, kIgnoreCase, &ordinal));
   EnsureFindBoxOpen();
-  EXPECT_EQ(ASCIIToUTF16("1 of 1"), GetMatchCountText());
+  EXPECT_EQ(ASCIIToUTF16("1/1"), GetMatchCountText());
 
   // Next, do a search in a second tab.
   chrome::AddTabAt(browser(), GURL(), -1, true);
@@ -1489,7 +1489,7 @@
   WebContents* web_contents_2 =
       browser()->tab_strip_model()->GetActiveWebContents();
   FindInPageASCII(web_contents_2, "text", kFwd, kIgnoreCase, &ordinal);
-  EXPECT_EQ(ASCIIToUTF16("1 of 1"), GetMatchCountText());
+  EXPECT_EQ(ASCIIToUTF16("1/1"), GetMatchCountText());
 
   // Go back to the first tab and verify that the match text is cleared.
   // text to "text".
@@ -1528,8 +1528,8 @@
   FindInPageASCII(web_contents_incognito, "foo", true, kIgnoreCase, NULL);
   EXPECT_EQ(ASCIIToUTF16("foo"),
       GetFindBarTextForBrowser(browser_incognito));
-  EXPECT_EQ(ASCIIToUTF16("1 of 2"),
-      GetFindBarMatchCountTextForBrowser(browser_incognito));
+  EXPECT_EQ(ASCIIToUTF16("1/2"),
+            GetFindBarMatchCountTextForBrowser(browser_incognito));
 
   // Close the find bar.
   FindTabHelper* find_tab_helper =
@@ -1544,7 +1544,7 @@
   observer.Wait();
   EXPECT_EQ(ASCIIToUTF16("foo"),
             GetFindBarTextForBrowser(browser_incognito));
-  EXPECT_EQ(ASCIIToUTF16("2 of 2"),
+  EXPECT_EQ(ASCIIToUTF16("2/2"),
             GetFindBarMatchCountTextForBrowser(browser_incognito));
 }
 
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 4f0345d..6d0d42b9 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/find_bar_host.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
@@ -42,20 +43,12 @@
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/painter.h"
+#include "ui/views/view_properties.h"
 #include "ui/views/view_targeter.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
 
-// These layout constants are all in dp.
-// The horizontal and vertical insets for the bar.
-const int kInteriorPadding = 8;
-// Default spacing between child views.
-const int kInterChildSpacing = 4;
-// Additional spacing around the separator.
-const int kSeparatorLeftSpacing = 12 - kInterChildSpacing;
-const int kSeparatorRightSpacing = 8 - kInterChildSpacing;
-
 // The default number of average characters that the text box will be.
 const int kDefaultCharWidth = 30;
 
@@ -161,15 +154,56 @@
       base::MakeUnique<views::ViewTargeter>(this));
   AddChildViewAt(match_count_text_, 1);
 
-  separator_->SetBorder(views::CreateEmptyBorder(0, kSeparatorLeftSpacing, 0,
-                                                 kSeparatorRightSpacing));
+  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+
   AddChildViewAt(separator_, 2);
 
+  // Normally we could space objects horizontally by simply passing a constant
+  // value to BoxLayout for between-child spacing.  But for the vector image
+  // buttons, we want the spacing to apply between the inner "glyph" portions
+  // of the buttons, ignoring the surrounding borders.  BoxLayout has no way
+  // to dynamically adjust for this, so instead of using between-child spacing,
+  // we place views directly adjacent, with horizontal margins on each view
+  // that will add up to the right spacing amounts.
+
+  const gfx::Insets horizontal_margin(
+      0,
+      provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL) / 2);
+  const gfx::Insets vector_button =
+      provider->GetInsetsMetric(views::INSETS_VECTOR_IMAGE_BUTTON);
+  const gfx::Insets vector_button_horizontal_margin(
+      0, horizontal_margin.left() - vector_button.left(), 0,
+      horizontal_margin.right() - vector_button.right());
+  const gfx::Insets toast_control_vertical_margin(
+      provider->GetDistanceMetric(DISTANCE_TOAST_CONTROL_VERTICAL), 0);
+  const gfx::Insets toast_label_vertical_margin(
+      provider->GetDistanceMetric(DISTANCE_TOAST_LABEL_VERTICAL), 0);
+  find_previous_button_->SetProperty(
+      views::kMarginsKey, new gfx::Insets(toast_control_vertical_margin +
+                                          vector_button_horizontal_margin));
+  find_next_button_->SetProperty(
+      views::kMarginsKey, new gfx::Insets(toast_control_vertical_margin +
+                                          vector_button_horizontal_margin));
+  close_button_->SetProperty(views::kMarginsKey,
+                             new gfx::Insets(toast_control_vertical_margin +
+                                             vector_button_horizontal_margin));
+  separator_->SetProperty(
+      views::kMarginsKey,
+      new gfx::Insets(toast_control_vertical_margin + horizontal_margin));
+  find_text_->SetProperty(
+      views::kMarginsKey,
+      new gfx::Insets(toast_control_vertical_margin + horizontal_margin));
+  match_count_text_->SetProperty(
+      views::kMarginsKey,
+      new gfx::Insets(toast_label_vertical_margin + horizontal_margin));
+
   find_text_->SetBorder(views::NullBorder());
 
-  views::BoxLayout* manager =
-      new views::BoxLayout(views::BoxLayout::kHorizontal,
-                           gfx::Insets(kInteriorPadding), kInterChildSpacing);
+  views::BoxLayout* manager = new views::BoxLayout(
+      views::BoxLayout::kHorizontal,
+      gfx::Insets(provider->GetInsetsMetric(INSETS_TOAST) - horizontal_margin),
+      0);
+
   SetLayoutManager(manager);
   manager->SetFlexForView(find_text_, 1);
 }
diff --git a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
index c292261..9631f017 100644
--- a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
@@ -34,6 +34,15 @@
          Get()->GetDistanceMetric(DISTANCE_CONTROL_TOTAL_VERTICAL_TEXT_PADDING);
 }
 
+gfx::Insets ChromeLayoutProvider::GetInsetsMetric(int metric) const {
+  switch (metric) {
+    case ChromeInsetsMetric::INSETS_TOAST:
+      return gfx::Insets(0, 8);
+    default:
+      return views::LayoutProvider::GetInsetsMetric(metric);
+  }
+}
+
 int ChromeLayoutProvider::GetDistanceMetric(int metric) const {
   switch (metric) {
     case DISTANCE_BUTTON_MINIMUM_WIDTH:
@@ -58,6 +67,10 @@
       return 20;
     case DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE:
       return 30;
+    case DISTANCE_TOAST_CONTROL_VERTICAL:
+      return 8;
+    case DISTANCE_TOAST_LABEL_VERTICAL:
+      return 12;
     default:
       return views::LayoutProvider::GetDistanceMetric(metric);
   }
diff --git a/chrome/browser/ui/views/harmony/chrome_layout_provider.h b/chrome/browser/ui/views/harmony/chrome_layout_provider.h
index c5185da..e931ff9 100644
--- a/chrome/browser/ui/views/harmony/chrome_layout_provider.h
+++ b/chrome/browser/ui/views/harmony/chrome_layout_provider.h
@@ -12,6 +12,11 @@
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/layout/layout_provider.h"
 
+enum ChromeInsetsMetric {
+  // Margins used by toasts.
+  INSETS_TOAST = views::VIEWS_INSETS_END,
+};
+
 enum ChromeDistanceMetric {
   // Default minimum width of a button.
   DISTANCE_BUTTON_MINIMUM_WIDTH = views::VIEWS_DISTANCE_END,
@@ -33,6 +38,10 @@
   // Horizontal indent of a subsection relative to related items above, e.g.
   // checkboxes below explanatory text/headings.
   DISTANCE_SUBSECTION_HORIZONTAL_INDENT,
+  // Vertical margin for controls in a toast.
+  DISTANCE_TOAST_CONTROL_VERTICAL,
+  // Vertical margin for labels in a toast.
+  DISTANCE_TOAST_LABEL_VERTICAL,
   // Horizontal spacing between controls that are logically unrelated.
   DISTANCE_UNRELATED_CONTROL_HORIZONTAL,
   // Larger horizontal spacing between unrelated controls.
@@ -54,6 +63,7 @@
   static int GetControlHeightForFont(const gfx::FontList& font);
 
   // views::LayoutProvider:
+  gfx::Insets GetInsetsMetric(int metric) const override;
   int GetDistanceMetric(int metric) const override;
   const views::TypographyProvider& GetTypographyProvider() const override;
 
diff --git a/chrome/browser/ui/views/harmony/harmony_layout_provider.cc b/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
index 03a1890..cbe4d3d 100644
--- a/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
@@ -18,6 +18,8 @@
     }
     case views::INSETS_VECTOR_IMAGE_BUTTON:
       return gfx::Insets(kHarmonyLayoutUnit / 4);
+    case INSETS_TOAST:
+      return gfx::Insets(0, kHarmonyLayoutUnit);
     default:
       return ChromeLayoutProvider::GetInsetsMetric(metric);
   }
@@ -73,9 +75,9 @@
       return kHarmonyLayoutUnit;
     case DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE:
       return kHarmonyLayoutUnit;
+    default:
+      return ChromeLayoutProvider::GetDistanceMetric(metric);
   }
-  NOTREACHED();
-  return 0;
 }
 
 views::GridLayout::Alignment
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
index 7118b9f..c15ebea 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
@@ -4,17 +4,13 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
 
-#include <stdint.h>
-
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/shell.h"
-#include "base/stl_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/display/display.h"
+#include "ui/display/display_layout.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
-#include "ui/events/devices/device_data_manager.h"
-#include "ui/events/devices/touchscreen_device.h"
 
 using content::BrowserThread;
 
@@ -27,9 +23,6 @@
          display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE;
 }
 
-// TODO(felixe): More context at crbug.com/738885
-const uint16_t kDeviceIds[] = {0x0457, 0x266e};
-
 }  // namespace
 
 OobeDisplayChooser::OobeDisplayChooser() : weak_ptr_factory_(this) {}
@@ -58,21 +51,18 @@
 void OobeDisplayChooser::MoveToTouchDisplay() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  const ui::DeviceDataManager* device_manager =
-      ui::DeviceDataManager::GetInstance();
-  for (const ui::TouchscreenDevice& device :
-       device_manager->GetTouchscreenDevices()) {
-    if (!base::ContainsValue(kDeviceIds, device.vendor_id))
-      continue;
+  const display::Displays& displays =
+      ash::Shell::Get()->display_manager()->active_only_display_list();
 
-    int64_t display_id =
-        device_manager->GetTargetDisplayForTouchDevice(device.id);
-    if (display_id == display::kInvalidDisplayId)
-      continue;
+  if (displays.size() <= 1)
+    return;
 
-    ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
-        display_id);
-    break;
+  for (const display::Display& display : displays) {
+    if (TouchSupportAvailable(display)) {
+      ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
+          display.id());
+      break;
+    }
   }
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
index 528fd1c..acf074ae 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
 
 #include <memory>
-#include <vector>
 
 #include "ash/display/display_configuration_controller.h"
 #include "ash/shell.h"
@@ -14,12 +13,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
 #include "ui/display/display_observer.h"
-#include "ui/display/manager/chromeos/touchscreen_util.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
-#include "ui/events/devices/device_data_manager.h"
-#include "ui/events/devices/touchscreen_device.h"
 
 namespace chromeos {
 
@@ -29,93 +25,63 @@
  public:
   OobeDisplayChooserTest() : ash::test::AshTestBase() {}
 
+  void SetUp() override {
+    ash::test::AshTestBase::SetUp();
+    display_manager_test_api_.reset(
+        new display::test::DisplayManagerTestApi(display_manager()));
+  }
+
+  void EnableTouch(int64_t id) {
+    display_manager_test_api_->SetTouchSupport(
+        id, display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE);
+  }
+
+  void DisableTouch(int64_t id) {
+    display_manager_test_api_->SetTouchSupport(
+        id, display::Display::TouchSupport::TOUCH_SUPPORT_UNAVAILABLE);
+  }
+
   int64_t GetPrimaryDisplay() {
     return display::Screen::GetScreen()->GetPrimaryDisplay().id();
   }
 
-  void UpdateTouchscreenDevices(const ui::TouchscreenDevice& touchscreen) {
-    std::vector<ui::TouchscreenDevice> devices{touchscreen};
-
-    ui::DeviceHotplugEventObserver* manager =
-        ui::DeviceDataManager::GetInstance();
-    manager->OnTouchscreenDevicesUpdated(devices);
-  }
-
  private:
+  std::unique_ptr<display::test::DisplayManagerTestApi>
+      display_manager_test_api_;
+
   DISALLOW_COPY_AND_ASSIGN(OobeDisplayChooserTest);
 };
 
-const uint16_t kWhitelistedId = 0x266e;
-
 }  // namespace
 
 TEST_F(OobeDisplayChooserTest, PreferTouchAsPrimary) {
-  // Setup 2 displays, second one is intended to be a touch display
-  std::vector<display::ManagedDisplayInfo> display_info;
-  display_info.push_back(
-      display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
-  display_info.push_back(
-      display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
-  display_manager()->OnNativeDisplaysChanged(display_info);
-  base::RunLoop().RunUntilIdle();
-
-  // Make sure the non-touch display is primary
-  ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
-
-  // Setup corresponding TouchscreenDevice object
-  ui::TouchscreenDevice touchscreen =
-      ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
-                            "Touchscreen", gfx::Size(800, 600), 1);
-  touchscreen.vendor_id = kWhitelistedId;
-  UpdateTouchscreenDevices(touchscreen);
-  base::RunLoop().RunUntilIdle();
-
-  // Associate touchscreen device with display
-  display_info[1].AddInputDevice(touchscreen.id);
-  display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
-  display_manager()->OnNativeDisplaysChanged(display_info);
-  base::RunLoop().RunUntilIdle();
-
   OobeDisplayChooser display_chooser;
-  EXPECT_EQ(1, GetPrimaryDisplay());
+
+  UpdateDisplay("3000x2000,800x600");
+  display::DisplayIdList ids = display_manager()->GetCurrentDisplayIdList();
+  DisableTouch(ids[0]);
+  EnableTouch(ids[1]);
+
+  EXPECT_EQ(ids[0], GetPrimaryDisplay());
   display_chooser.TryToPlaceUiOnTouchDisplay();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, GetPrimaryDisplay());
+
+  EXPECT_EQ(ids[1], GetPrimaryDisplay());
 }
 
-TEST_F(OobeDisplayChooserTest, DontSwitchFromTouch) {
-  // Setup 2 displays, second one is intended to be a touch display
-  std::vector<display::ManagedDisplayInfo> display_info;
-  display_info.push_back(
-      display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
-  display_info.push_back(
-      display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
-  display_info[0].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
-  display_manager()->OnNativeDisplaysChanged(display_info);
-  base::RunLoop().RunUntilIdle();
-
-  // Make sure the non-touch display is primary
-  ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
-
-  // Setup corresponding TouchscreenDevice object
-  ui::TouchscreenDevice touchscreen =
-      ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
-                            "Touchscreen", gfx::Size(800, 600), 1);
-  touchscreen.vendor_id = kWhitelistedId;
-  UpdateTouchscreenDevices(touchscreen);
-  base::RunLoop().RunUntilIdle();
-
-  // Associate touchscreen device with display
-  display_info[1].AddInputDevice(touchscreen.id);
-  display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
-  display_manager()->OnNativeDisplaysChanged(display_info);
-  base::RunLoop().RunUntilIdle();
-
+TEST_F(OobeDisplayChooserTest, AddingSecondTouchDisplayShouldbeNOP) {
   OobeDisplayChooser display_chooser;
-  EXPECT_EQ(1, GetPrimaryDisplay());
+
+  UpdateDisplay("3000x2000,800x600");
+  display::DisplayIdList ids = display_manager()->GetCurrentDisplayIdList();
+  EnableTouch(ids[0]);
+  EnableTouch(ids[1]);
+
+  EXPECT_EQ(ids[0], GetPrimaryDisplay());
   display_chooser.TryToPlaceUiOnTouchDisplay();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, GetPrimaryDisplay());
+
+  EXPECT_EQ(ids[0], GetPrimaryDisplay());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index 94f7115..d2db7e5 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -226,12 +226,17 @@
   return path;
 }
 
-bool IsRemoraRequisitioned() {
-  policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
-      g_browser_process->platform_part()
-          ->browser_policy_connector_chromeos()
-          ->GetDeviceCloudPolicyManager();
-  return policy_manager && policy_manager->IsRemoraRequisition();
+bool IsKeyboardConnected() {
+  const std::vector<ui::InputDevice>& keyboards =
+      ui::InputDeviceManager::GetInstance()->GetKeyboardDevices();
+  for (const ui::InputDevice& keyboard : keyboards) {
+    if (keyboard.type == ui::INPUT_DEVICE_INTERNAL ||
+        keyboard.type == ui::INPUT_DEVICE_EXTERNAL) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 }  // namespace
@@ -371,7 +376,7 @@
 
   // TODO(felixe): Display iteration and primary display selection not supported
   // in Mash. See http://crbug.com/720917.
-  if (!ash_util::IsRunningInMash() && IsRemoraRequisitioned())
+  if (!ash_util::IsRunningInMash() && !IsKeyboardConnected())
     oobe_display_chooser_ = base::MakeUnique<OobeDisplayChooser>();
 }
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc b/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
index 3d2ee7e..517e61f 100644
--- a/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
@@ -154,7 +154,10 @@
         base::BindOnce(&MediaRouterFileDialog::ReportFileFormat,
                        base::Unretained(this), selected_file_->local_path));
 
-    // TODO(paezagon): Report file media length.
+    cancelable_task_tracker_.PostTask(
+        task_runner_.get(), FROM_HERE,
+        base::BindOnce(&MediaRouterFileDialog::ReportFileSize,
+                       base::Unretained(this), selected_file_->local_path));
   } else {
     VLOG(1) << "MediaRouterFileDialog did not report file information; no file "
                "to report.";
@@ -187,6 +190,16 @@
           reinterpret_cast<const uint8_t*>(buffer), read));
 }
 
+void MediaRouterFileDialog::ReportFileSize(const base::FilePath& filename) {
+  int64_t size;
+  if (base::GetFileSize(filename, &size)) {
+    MediaRouterMetrics::RecordMediaRouterFileSize(size);
+  } else {
+    VLOG(1) << "Can't get file size for file: " << filename.LossyDisplayName()
+            << ".";
+  }
+}
+
 void MediaRouterFileDialog::FileSelected(const base::FilePath& path,
                                          int index,
                                          void* params) {
diff --git a/chrome/browser/ui/webui/media_router/media_router_file_dialog.h b/chrome/browser/ui/webui/media_router/media_router_file_dialog.h
index 31bb5f9e..29e8bac5 100644
--- a/chrome/browser/ui/webui/media_router/media_router_file_dialog.h
+++ b/chrome/browser/ui/webui/media_router/media_router_file_dialog.h
@@ -114,6 +114,9 @@
   // Reports the format of a file to the UMA stats.
   void ReportFileFormat(const base::FilePath& filename);
 
+  // Reports the size of a file to the UMA stats.
+  void ReportFileSize(const base::FilePath& filename);
+
   // Overridden from SelectFileDialog::Listener:
   void FileSelected(const base::FilePath& path,
                     int index,
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 b63e1ec26..5f8c4f8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -88,8 +88,6 @@
 #include "third_party/icu/source/i18n/unicode/ulocdata.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
index 22867769..303cd17 100644
--- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
-#include "chrome/browser/chromeos/printing/printers_manager.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
 #include "chrome/common/chrome_switches.h"
@@ -74,7 +74,8 @@
 class PrinterBackendProxyChromeos : public PrinterBackendProxy {
  public:
   explicit PrinterBackendProxyChromeos(Profile* profile)
-      : prefs_(chromeos::PrintersManagerFactory::GetForBrowserContext(profile)),
+      : prefs_(chromeos::SyncedPrintersManagerFactory::GetForBrowserContext(
+            profile)),
         printer_configurer_(chromeos::PrinterConfigurer::Create(profile)),
         weak_factory_(this) {
     // Construct the CupsPrintJobManager to listen for printing events.
@@ -94,7 +95,7 @@
   };
 
   void EnumeratePrinters(const EnumeratePrintersCallback& cb) override {
-    // PrintersManager is not thread safe and must be called from the UI
+    // SyncedPrintersManager is not thread safe and must be called from the UI
     // thread.
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -183,7 +184,7 @@
     cb.Run(nullptr);
   }
 
-  chromeos::PrintersManager* prefs_;
+  chromeos::SyncedPrintersManager* prefs_;
   scoped_refptr<chromeos::printing::PpdProvider> ppd_provider_;
   std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer_;
   base::WeakPtrFactory<PrinterBackendProxyChromeos> weak_factory_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
index c21e51d..d23a203 100644
--- a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
@@ -114,6 +114,10 @@
   web_ui()->RegisterMessageCallback(
       "selectImage", base::Bind(&ChangePictureHandler::HandleSelectImage,
                                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "requestSelectedImage",
+      base::Bind(&ChangePictureHandler::HandleRequestSelectedImage,
+                 base::Unretained(this)));
 }
 
 void ChangePictureHandler::OnJavascriptAllowed() {
@@ -322,6 +326,11 @@
     ImageDecoder::Cancel(this);
 }
 
+void ChangePictureHandler::HandleRequestSelectedImage(
+    const base::ListValue* args) {
+  SendSelectedImage();
+}
+
 void ChangePictureHandler::FileSelected(const base::FilePath& path,
                                         int index,
                                         void* params) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
index a2132822..399cf3e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
@@ -87,6 +87,9 @@
   // Selects one of the available images as user's.
   void HandleSelectImage(const base::ListValue* args);
 
+  // Requests the currently selected image.
+  void HandleRequestSelectedImage(const base::ListValue* args);
+
   // SelectFileDialog::Delegate implementation.
   void FileSelected(const base::FilePath& path,
                     int index,
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index dcfc342..11bd680 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
 #include "chrome/browser/chromeos/printing/printer_info.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/usb_printer_detector.h"
 #include "chrome/browser/chromeos/printing/usb_printer_detector_factory.h"
 #include "chrome/browser/download/download_prefs.h"
@@ -186,7 +186,8 @@
   CHECK(args->GetString(0, &callback_id));
 
   std::vector<std::unique_ptr<Printer>> printers =
-      PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters();
+      SyncedPrintersManagerFactory::GetForBrowserContext(profile_)
+          ->GetPrinters();
 
   auto printers_list = base::MakeUnique<base::ListValue>();
   for (const std::unique_ptr<Printer>& printer : printers) {
@@ -208,7 +209,7 @@
 
   std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
   printer->set_display_name(printer_name);
-  PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
+  SyncedPrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
       std::move(printer));
 }
 
@@ -217,8 +218,8 @@
   std::string printer_name;
   CHECK(args->GetString(0, &printer_id));
   CHECK(args->GetString(1, &printer_name));
-  PrintersManager* prefs =
-      PrintersManagerFactory::GetForBrowserContext(profile_);
+  SyncedPrintersManager* prefs =
+      SyncedPrintersManagerFactory::GetForBrowserContext(profile_);
   auto printer = prefs->GetPrinter(printer_id);
   if (!printer)
     return;
@@ -419,7 +420,8 @@
     case PrinterSetupResult::kSuccess: {
       UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterAdded",
                                 printer->GetProtocol(), Printer::kProtocolMax);
-      auto* manager = PrintersManagerFactory::GetForBrowserContext(profile_);
+      auto* manager =
+          SyncedPrintersManagerFactory::GetForBrowserContext(profile_);
       manager->PrinterInstalled(*printer);
       manager->RegisterPrinter(std::move(printer));
       break;
@@ -571,8 +573,8 @@
   std::unique_ptr<base::ListValue> printers_list =
       base::MakeUnique<base::ListValue>();
   // Filter out already-configured printers as we go.
-  PrintersManager* printers_manager =
-      PrintersManagerFactory::GetForBrowserContext(profile_);
+  SyncedPrintersManager* printers_manager =
+      SyncedPrintersManagerFactory::GetForBrowserContext(profile_);
   if (printers_manager != nullptr) {
     for (const auto& printer : printers) {
       if (printers_manager->GetPrinter(printer.id()).get() == nullptr) {
@@ -581,7 +583,7 @@
     }
   } else {
     LOG(WARNING) << "Failing to get available printers because no "
-                    "PrintersManager exists.";
+                    "SyncedPrintersManager exists.";
   }
   FireWebUIListener("on-printer-discovered", *printers_list);
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
index dbd70059..1fea2b8 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "content/public/browser/web_ui.h"
 #include "ui/events/devices/input_device_manager.h"
@@ -31,12 +30,17 @@
 namespace chromeos {
 namespace settings {
 
-KeyboardHandler::KeyboardHandler(content::WebUI* webui)
-    : profile_(Profile::FromWebUI(webui)), observer_(this) {}
+const char KeyboardHandler::kShowKeysChangedName[] = "show-keys-changed";
 
-KeyboardHandler::~KeyboardHandler() {
+void KeyboardHandler::TestAPI::Initialize() {
+  base::ListValue args;
+  handler_->HandleInitialize(&args);
 }
 
+KeyboardHandler::KeyboardHandler() : observer_(this) {}
+
+KeyboardHandler::~KeyboardHandler() = default;
+
 void KeyboardHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
       "initializeKeyboardSettings",
@@ -71,11 +75,16 @@
 }
 
 void KeyboardHandler::UpdateShowKeys() {
-  const base::Value has_caps_lock(HasExternalKeyboard());
+  // kHasChromeOSKeyboard will be unset on Chromebooks that have standalone Caps
+  // Lock keys.
+  const base::Value has_caps_lock(
+      HasExternalKeyboard() ||
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kHasChromeOSKeyboard));
   const base::Value has_diamond_key(
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kHasChromeOSDiamondKey));
-  FireWebUIListener("show-keys-changed", has_caps_lock, has_diamond_key);
+  FireWebUIListener(kShowKeysChangedName, has_caps_lock, has_diamond_key);
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
index 7ac9d44..d1afe730 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
@@ -14,16 +14,10 @@
 class ListValue;
 }
 
-namespace content {
-class WebUI;
-}
-
 namespace ui {
 class InputDeviceManager;
 }
 
-class Profile;
-
 namespace chromeos {
 namespace settings {
 
@@ -32,7 +26,24 @@
     : public ::settings::SettingsPageUIHandler,
       public ui::InputDeviceEventObserver {
  public:
-  explicit KeyboardHandler(content::WebUI* webui);
+  // Name of the message sent to WebUI when the keys that should be shown
+  // change.
+  static const char kShowKeysChangedName[];
+
+  // Class used by tests to interact with KeyboardHandler internals.
+  class TestAPI {
+   public:
+    explicit TestAPI(KeyboardHandler* handler) { handler_ = handler; }
+
+    // Simulates a request from WebUI to initialize the page.
+    void Initialize();
+
+   private:
+    KeyboardHandler* handler_;  // Not owned.
+    DISALLOW_COPY_AND_ASSIGN(TestAPI);
+  };
+
+  KeyboardHandler();
   ~KeyboardHandler() override;
 
   // SettingsPageUIHandler implementation.
@@ -54,8 +65,6 @@
   // system status.
   void UpdateShowKeys();
 
-  Profile* profile_;  // Weak pointer.
-
   ScopedObserver<ui::InputDeviceManager, KeyboardHandler> observer_;
 
   DISALLOW_COPY_AND_ASSIGN(KeyboardHandler);
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc
new file mode 100644
index 0000000..7d83749
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/observer_list.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/test/device_data_manager_test_api.h"
+
+namespace chromeos {
+namespace settings {
+
+namespace {
+
+class TestKeyboardHandler : public KeyboardHandler {
+ public:
+  // Pull WebUIMessageHandler::set_web_ui() into public so tests can call it.
+  using KeyboardHandler::set_web_ui;
+};
+
+}  // namespace
+
+class KeyboardHandlerTest : public testing::Test {
+ public:
+  KeyboardHandlerTest() : handler_test_api_(&handler_) {
+    handler_.set_web_ui(&web_ui_);
+    handler_.RegisterMessages();
+    handler_.AllowJavascriptForTesting();
+
+    // Make sure that we start out without any keyboards reported.
+    device_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>());
+  }
+
+ protected:
+  // Updates out-params from the last message sent to WebUI about a change to
+  // which keys should be shown. False is returned if the message was invalid or
+  // not found.
+  bool GetLastShowKeysChangedMessage(bool* has_caps_lock_out,
+                                     bool* has_diamond_key_out)
+      WARN_UNUSED_RESULT {
+    for (auto it = web_ui_.call_data().rbegin();
+         it != web_ui_.call_data().rend(); ++it) {
+      const content::TestWebUI::CallData* data = it->get();
+      std::string name;
+      if (data->function_name() != "cr.webUIListenerCallback" ||
+          !data->arg1()->GetAsString(&name) ||
+          name != KeyboardHandler::kShowKeysChangedName) {
+        continue;
+      }
+      return data->arg2()->GetAsBoolean(has_caps_lock_out) &&
+             data->arg3()->GetAsBoolean(has_diamond_key_out);
+    }
+    return false;
+  }
+
+  // Returns true if the last keys-changed message reported that a Caps Lock key
+  // is present and false otherwise. A failure is added if a message wasn't
+  // found.
+  bool HasCapsLock() {
+    bool has_caps_lock = false, has_diamond_key = false;
+    if (!GetLastShowKeysChangedMessage(&has_caps_lock, &has_diamond_key)) {
+      ADD_FAILURE() << "Didn't get " << KeyboardHandler::kShowKeysChangedName;
+      return false;
+    }
+    return has_caps_lock;
+  }
+
+  // Returns true if the last keys-changed message reported that a "diamond" key
+  // is present and false otherwise. A failure is added if a message wasn't
+  // found.
+  bool HasDiamondKey() {
+    bool has_caps_lock = false, has_diamond_key = false;
+    if (!GetLastShowKeysChangedMessage(&has_caps_lock, &has_diamond_key)) {
+      ADD_FAILURE() << "Didn't get " << KeyboardHandler::kShowKeysChangedName;
+      return false;
+    }
+    return has_diamond_key;
+  }
+
+  ui::test::DeviceDataManagerTestAPI device_test_api_;
+  content::TestWebUI web_ui_;
+  TestKeyboardHandler handler_;
+  KeyboardHandler::TestAPI handler_test_api_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(KeyboardHandlerTest);
+};
+
+TEST_F(KeyboardHandlerTest, DefaultKeys) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kHasChromeOSKeyboard);
+  handler_test_api_.Initialize();
+  EXPECT_FALSE(HasCapsLock());
+  EXPECT_FALSE(HasDiamondKey());
+}
+
+TEST_F(KeyboardHandlerTest, NonChromeOSKeyboard) {
+  // If kHasChromeOSKeyboard isn't passed, we should assume there's a Caps Lock
+  // key.
+  handler_test_api_.Initialize();
+  EXPECT_TRUE(HasCapsLock());
+  EXPECT_FALSE(HasDiamondKey());
+}
+
+TEST_F(KeyboardHandlerTest, ExternalKeyboard) {
+  // An internal keyboard shouldn't change the defaults.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kHasChromeOSKeyboard);
+  device_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{
+      {1, ui::INPUT_DEVICE_INTERNAL, "internal keyboard"}});
+  handler_test_api_.Initialize();
+  EXPECT_FALSE(HasCapsLock());
+  EXPECT_FALSE(HasDiamondKey());
+
+  // Simulate an external keyboard being connected. We should assume there's a
+  // Caps Lock key now.
+  device_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{
+      {1, ui::INPUT_DEVICE_EXTERNAL, "external keyboard"}});
+  device_test_api_.NotifyObserversKeyboardDeviceConfigurationChanged();
+  EXPECT_TRUE(HasCapsLock());
+  EXPECT_FALSE(HasDiamondKey());
+
+  // Disconnect the external keyboard and check that the key goes away.
+  device_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>());
+  device_test_api_.NotifyObserversKeyboardDeviceConfigurationChanged();
+  EXPECT_FALSE(HasCapsLock());
+  EXPECT_FALSE(HasDiamondKey());
+}
+
+TEST_F(KeyboardHandlerTest, DiamondKey) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kHasChromeOSKeyboard);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kHasChromeOSDiamondKey);
+  handler_test_api_.Initialize();
+  EXPECT_FALSE(HasCapsLock());
+  EXPECT_TRUE(HasDiamondKey());
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_power_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/device_power_handler_unittest.cc
index acac9ab..c5a0b4c1 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_power_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_power_handler_unittest.cc
@@ -70,7 +70,6 @@
       const content::TestWebUI::CallData* data = it->get();
       std::string name;
       const base::DictionaryValue* dict = nullptr;
-      data->arg1()->GetAsString(&name);
       if (data->function_name() != "cr.webUIListenerCallback" ||
           !data->arg1()->GetAsString(&name) ||
           name != PowerHandler::kPowerManagementSettingsChangedName) {
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index e23f6f87..d0e730c 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1505,10 +1505,10 @@
     {"printingManageCloudPrintDevices",
      IDS_SETTINGS_PRINTING_MANAGE_CLOUD_PRINT_DEVICES},
     {"cloudPrintersTitle", IDS_SETTINGS_PRINTING_CLOUD_PRINTERS},
-    {"cloudPrintersTitleDescription",
-     IDS_SETTINGS_PRINTING_CLOUD_PRINTERS_DESCRIPTION},
 #if defined(OS_CHROMEOS)
     {"cupsPrintersTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTERS},
+    {"cupsPrintersLearnMoreLabel",
+     IDS_SETTINGS_PRINTING_CUPS_PRINTERS_LEARN_MORE_LABEL},
     {"addCupsPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER},
     {"cupsPrinterDetails", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_DETAILS},
     {"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE},
@@ -1577,6 +1577,8 @@
   html_source->AddBoolean("showCupsPrintingFeatures",
                           !base::CommandLine::ForCurrentProcess()->HasSwitch(
                               ::switches::kDisableNativeCups));
+  html_source->AddString("printingCUPSPrintLearnMoreUrl",
+                         chrome::kCupsPrintLearnMoreURL);
 #endif
 }
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 1e586a1e..b29198f5 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -165,7 +165,7 @@
   AddSettingsPageUIHandler(
       base::MakeUnique<chromeos::settings::FingerprintHandler>(profile));
   AddSettingsPageUIHandler(
-      base::MakeUnique<chromeos::settings::KeyboardHandler>(web_ui));
+      base::MakeUnique<chromeos::settings::KeyboardHandler>());
   AddSettingsPageUIHandler(
       base::MakeUnique<chromeos::settings::PointerHandler>());
   AddSettingsPageUIHandler(
diff --git a/chrome/browser/ui/webui/system_info_ui.cc b/chrome/browser/ui/webui/system_info_ui.cc
index ac2190e..e478be7 100644
--- a/chrome/browser/ui/webui/system_info_ui.cc
+++ b/chrome/browser/ui/webui/system_info_ui.cc
@@ -23,12 +23,12 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/feedback/system_logs/about_system_logs_fetcher.h"
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index c3c57bf..4a382e2 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -231,6 +231,9 @@
     // TEMPORARY: Compositor state for debugging BeginMainFrame renderer hang.
     // TODO(sunnyps): Remove after fixing https://crbug.com/622080
     {kBeginMainFrameHangCompositorState, crash_keys::kSmallSize},
+
+    // TODO(asvitkine): Remove after fixing https://crbug.com/736675
+    {"bad_histogram", kMediumSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 46c07aa..950f544e 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -139,6 +139,8 @@
 // TODO(xdai): Change it to chrome://settings/cupsPrinters after M56 since MD
 // settings is going to launch in Chrome OS in M57.
 const char kChromeUIMdCupsSettingsURL[] = "chrome://md-settings/cupsPrinters";
+const char kCupsPrintLearnMoreURL[] =
+    "https://support.google.com/chromebook?p=chromebook_printing";
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 9ba18fdc..ec9a397 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -127,6 +127,7 @@
 extern const char kChromeUITermsOemURL[];
 extern const char kChromeUIUserImageURL[];
 extern const char kChromeUIMdCupsSettingsURL[];
+extern const char kCupsPrintLearnMoreURL[];
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index efbc55d8..8f0a534 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3623,6 +3623,7 @@
       "../browser/media_galleries/win/mtp_device_object_enumerator_unittest.cc",
       "../browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc",
       "../browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc",
+      "../browser/resource_coordinator/tab_manager_stats_collector_unittest.cc",
       "../browser/resource_coordinator/tab_manager_unittest.cc",
       "../browser/resource_coordinator/tab_manager_web_contents_data_unittest.cc",
 
diff --git a/chrome/test/data/extensions/lock_screen_apps/data_provider/background.js b/chrome/test/data/extensions/lock_screen_apps/data_provider/background.js
new file mode 100644
index 0000000..1bd759eb
--- /dev/null
+++ b/chrome/test/data/extensions/lock_screen_apps/data_provider/background.js
@@ -0,0 +1,91 @@
+// 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.
+
+var callbackPass = chrome.test.callbackPass;
+var callbackFail = chrome.test.callbackFail;
+
+chrome.app.runtime.onLaunched.addListener(function(data) {
+  chrome.test.runTests([
+    function launchTest() {
+      chrome.test.assertTrue(!!data);
+      chrome.test.assertTrue(!!data.actionData);
+      chrome.test.assertEq('new_note', data.actionData.actionType);
+      chrome.test.assertTrue(data.actionData.isLockScreenAction);
+
+      chrome.app.window.create('test.html', {
+        lockScreenAction: 'new_note'
+      }, chrome.test.callbackPass(function(createdWindow) {
+        chrome.test.listenOnce(createdWindow.onClosed,
+                               chrome.test.callbackPass());
+      }));
+    }
+  ]);
+});
+
+// Event expected to be fired in regular user context when user session starts,
+// or gets unlocked, in case there are data items in lock screen storage.
+// The app will run set of tests verifying that the state of the app's lock
+// storage matches to the state set in the app's test.html window (i.e. in tests
+// run when the app is launched to handle new lock screen note action).
+chrome.lockScreen.data.onDataItemsAvailable.addListener(function() {
+  var itemsInfo = [];
+
+  var sortItems = function(infoList) {
+    return  itemsInfo.sort(function(lhs, rhs) {
+      if (lhs.content == rhs.content)
+        return 0;
+      return lhs.content < rhs.content ? -1 : 1;
+    });
+  };
+
+  chrome.test.runTests([
+    function createNotAvailable() {
+      chrome.test.assertFalse(!!chrome.lockScreen.data.create);
+      chrome.test.succeed();
+    },
+
+    function getAll() {
+      chrome.lockScreen.data.getAll(callbackPass(function(items) {
+        chrome.test.assertEq(3, items.length);
+
+        items.forEach(function(item) {
+          var itemInfo = {id: item.id};
+          chrome.lockScreen.data.getContent(item.id, callbackPass(function(
+              content) {
+            var decoder = new TextDecoder();
+            itemInfo.content = decoder.decode(content);
+            itemsInfo.push(itemInfo);
+          }));
+        });
+      }));
+    },
+
+    function testItemInfo() {
+      chrome.test.assertEq([{
+        content: ''
+      }, {
+        content: '1 - Created by the app.'
+      }, {
+        content: '2 - Created and updated by the app - final.'
+      }], sortItems(itemsInfo).map(function(item) {
+        return {
+          content: item.content
+        }
+      }));
+      chrome.test.succeed();
+    },
+
+    function deleteItem() {
+      var sortedItems = sortItems(itemsInfo);
+      // Sanity check for test preconditions.
+      chrome.test.assertEq(3, sortedItems.length);
+
+      chrome.lockScreen.data.delete(itemsInfo[0].id, callbackPass(function() {
+        chrome.lockScreen.data.getAll(callbackPass(function(info) {
+          chrome.test.assertEq(2, info.length);
+        }));
+      }));
+    }
+  ]);
+});
diff --git a/chrome/test/data/extensions/lock_screen_apps/data_provider/manifest.json b/chrome/test/data/extensions/lock_screen_apps/data_provider/manifest.json
new file mode 100644
index 0000000..9a8fd29
--- /dev/null
+++ b/chrome/test/data/extensions/lock_screen_apps/data_provider/manifest.json
@@ -0,0 +1,16 @@
+{
+   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA02u2EolNQq2mp/jmpE6tlctinxHNThc5FIsleGtPWsyCOxndLPlNqUKnXpAzllyUahwqtP+hYeYStiq3+0pcSEz4i9dGBF/DRKZ09LvKiDQB26Xvb8+IDxlqwe4Z/z9yQLB7xyg977z5Xi09DMVEtUfyuJTvtWQMcTpwyh5dEhK/F8R/lPrzmMU9bvidzRtruLuXd/DyQOPpNqSQTbN3Tg3dD4xDO54N+4ld8Pot6Nob0P0Yn/wm4c6Ct+5qwESaMP3C3WIIRDJ41rU4RgRSioxEYSdTtyrnO2qSK6KVKEFDmRp5hMxOn0r7C3CsdwMVMS3FXctjNia+1M3/2AMn/wIDAQAB",
+  "name": "Test lock screen app",
+  "version": "0.1",
+  "manifest_version": 2,
+  "permissions": ["lockScreen"],
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  },
+  "action_handlers": [{
+    "action": "new_note",
+    "enabled_on_lock_screen": true
+  }]
+}
diff --git a/chrome/test/data/extensions/lock_screen_apps/data_provider/test.html b/chrome/test/data/extensions/lock_screen_apps/data_provider/test.html
new file mode 100644
index 0000000..682e3d21
--- /dev/null
+++ b/chrome/test/data/extensions/lock_screen_apps/data_provider/test.html
@@ -0,0 +1,5 @@
+<html>
+<head>
+<script src="test.js"> </script>
+</head>
+</html>
diff --git a/chrome/test/data/extensions/lock_screen_apps/data_provider/test.js b/chrome/test/data/extensions/lock_screen_apps/data_provider/test.js
new file mode 100644
index 0000000..2c07aba
--- /dev/null
+++ b/chrome/test/data/extensions/lock_screen_apps/data_provider/test.js
@@ -0,0 +1,99 @@
+// 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.
+
+var callbackPass = chrome.test.callbackPass;
+var callbackFail = chrome.test.callbackFail;
+
+var createdIds = [];
+var deletedIds = [];
+
+chrome.test.runTests([
+  function createNote() {
+    chrome.lockScreen.data.create(callbackPass(function(item) {
+      chrome.test.assertEq(-1, createdIds.indexOf(item.id));
+      createdIds.push(item.id);
+
+      var encoder = new TextEncoder();
+      var text = '1 - Created by the app.';
+      chrome.lockScreen.data.setContent(
+          item.id, encoder.encode(text).buffer, callbackPass());
+    }));
+  },
+
+  function createAndResetNoteContent() {
+    chrome.lockScreen.data.create(callbackPass(function(item) {
+      chrome.test.assertEq(-1, createdIds.indexOf(item.id));
+      createdIds.push(item.id);
+
+      var encoder = new TextEncoder();
+      var text = '2 - Created and updated by the app - initial.';
+      chrome.lockScreen.data.setContent(
+          item.id, encoder.encode(text).buffer, callbackPass(function() {
+        var text = '2 - Created and updated by the app - final.';
+        chrome.lockScreen.data.setContent(
+            item.id, encoder.encode(text).buffer, callbackPass());
+      }));
+    }));
+  },
+
+  function createAndDeleteNote() {
+    chrome.lockScreen.data.create(callbackPass(function(item) {
+      chrome.test.assertEq(-1, createdIds.indexOf(item.id));
+      createdIds.push(item.id);
+      deletedIds.push(item.id);
+
+      var encoder = new TextEncoder();
+      var text = '3 - Item deleted by the app';
+      chrome.lockScreen.data.setContent(
+          item.id, encoder.encode(text).buffer, callbackPass(function() {
+        chrome.lockScreen.data.delete(item.id, callbackPass(function() {
+          chrome.lockScreen.data.setContent(
+              item.id, encoder.encode('text').buffer,
+              callbackFail('Not found'));
+        }));
+      }));
+    }));
+  },
+
+  function createEmptyNote() {
+    chrome.lockScreen.data.create(callbackPass(function(item) {
+      chrome.test.assertEq(-1, createdIds.indexOf(item.id));
+      createdIds.push(item.id);
+    }));
+  },
+
+  function getAll() {
+    var sortItems = function(infoList) {
+      return  infoList.sort(function(lhs, rhs) {
+        return lhs.id < rhs.id ? -1 : 1;
+      });
+    };
+
+    chrome.lockScreen.data.getAll(callbackPass(function(items) {
+      chrome.test.assertEq(
+          sortItems(createdIds.filter(function(id) {
+            return deletedIds.indexOf(id) < 0;
+          }).map(function(id) {
+            return {id: id};
+          })),
+          sortItems(items));
+    }));
+  },
+
+  function reportReadyToClose() {
+    // Notify the test runner the app window is ready to be closed - if the test
+    // runner replies with 'close', close the current app window. Otherwise, the
+    // test runner will close the window itself.
+    // NOTE: Reporting the test success should not wait for this - the test
+    //     runner should be notified of test run success before responding to
+    //     this message to avoid test done message being disregarded due to app
+    //     window clusure.
+    chrome.test.sendMessage('readyToClose', function(response) {
+      if (response === 'close')
+        chrome.app.window.current().close();
+    });
+
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index aa5d175..48cc53b4 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -107,10 +107,19 @@
    * to add a printer.
    */
   test('DiscoveryShowing', function() {
-    assertFalse(!!dialog.$$('add-printer-manufacturer-model-dialog'));
-    assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
-    assertFalse(!!dialog.$$('add-printer-manually-dialog'));
-    assertTrue(!!dialog.$$('add-printer-discovery-dialog'));
+    return PolymerTest.flushTasks().then(function() {
+      // Discovery is showing.
+      assertTrue(dialog.showDiscoveryDialog_);
+      assertTrue(!!dialog.$$('add-printer-discovery-dialog'));
+
+      // All other components are hidden.
+      assertFalse(dialog.showManufacturerDialog_);
+      assertFalse(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+      assertFalse(dialog.showConfiguringDialog_);
+      assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
+      assertFalse(dialog.showManuallyAddDialog_);
+      assertFalse(!!dialog.$$('add-printer-manually-dialog'));
+    });
   });
 
   /**
@@ -137,8 +146,17 @@
     return cupsPrintersBrowserProxy.
         whenCalled('getCupsPrinterManufacturersList').
         then(function() {
-          // TODO(skau): Verify other dialogs are hidden.
+          return PolymerTest.flushTasks();
+        }).
+        then(function() {
+          // Showing model selection.
+          assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
           assertTrue(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+
+          assertTrue(dialog.showManufacturerDialog_);
+          assertFalse(dialog.showConfiguringDialog_);
+          assertFalse(dialog.showManuallyAddDialog_);
+          assertFalse(dialog.showDiscoveryDialog_);
         });
   });
 
diff --git a/chrome/test/data/webui/settings/people_page_change_picture_test.js b/chrome/test/data/webui/settings/people_page_change_picture_test.js
index 3ffc0ca..de8e9bb 100644
--- a/chrome/test/data/webui/settings/people_page_change_picture_test.js
+++ b/chrome/test/data/webui/settings/people_page_change_picture_test.js
@@ -16,6 +16,7 @@
       'selectProfileImage',
       'photoTaken',
       'chooseFile',
+      'requestSelectedImage',
     ]);
   };
 
@@ -76,6 +77,11 @@
     chooseFile: function() {
       this.methodCalled('chooseFile');
     },
+
+    /** @override */
+    requestSelectedImage: function() {
+      this.methodCalled('requestSelectedImage');
+    },
   };
 
   function registerChangePictureTests() {
@@ -187,6 +193,8 @@
       });
 
       test('ChangePictureOldImage', function() {
+        assertFalse(!!changePicture.selectedItem_);
+
         // By default there is no old image and the element is hidden.
         var oldImage = crPictureList.$.oldImage;
         assertTrue(!!oldImage);
@@ -195,15 +203,20 @@
         cr.webUIListenerCallback('old-image-changed', 'fake-old-image.jpg');
         Polymer.dom.flush();
 
-        // Expect the old image to be selected once an old image is sent via
-        // the native interface.
-        expectEquals(CrPicture.SelectionTypes.OLD,
-                     changePicture.selectedItem_.dataset.type);
-        expectFalse(oldImage.hidden);
-        expectFalse(crPicturePane.cameraActive_);
-        var discard = crPicturePane.$$('#discard');
-        assertTrue(!!discard);
-        expectFalse(discard.hidden);
+        return new Promise(function(resolve) {
+          changePicture.async(resolve);
+        }).then(function() {
+          assertTrue(!!changePicture.selectedItem_);
+          // Expect the old image to be selected once an old image is sent via
+          // the native interface.
+          expectEquals(CrPicture.SelectionTypes.OLD,
+                       changePicture.selectedItem_.dataset.type);
+          expectFalse(oldImage.hidden);
+          expectFalse(crPicturePane.cameraActive_);
+          var discard = crPicturePane.$$('#discard');
+          assertTrue(!!discard);
+          expectFalse(discard.hidden);
+        });
       });
 
       test('ChangePictureSelectFirstDefaultImage', function() {
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index f7e18dc..f384f5f 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -374,6 +374,13 @@
 // for any type of user. Should be used only for testing.
 const char kForceHappinessTrackingSystem[] = "force-happiness-tracking-system";
 
+// If set, the system is a Chromebook with a "standard Chrome OS keyboard",
+// which generally means one with a Search key in the standard Caps Lock
+// location above the Left Shift key. It should be unset for Chromebooks with
+// both Search and Caps Lock keys (e.g. stout) and for devices like Chromeboxes
+// that only use external keyboards.
+const char kHasChromeOSKeyboard[] = "has-chromeos-keyboard";
+
 // If true, the Chromebook has a keyboard with a diamond key.
 const char kHasChromeOSDiamondKey[] = "has-chromeos-diamond-key";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index be722daf..d837ac860 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -114,6 +114,7 @@
 CHROMEOS_EXPORT extern const char kGuestWallpaperSmall[];
 CHROMEOS_EXPORT extern const char kForceHappinessTrackingSystem[];
 CHROMEOS_EXPORT extern const char kHasChromeOSDiamondKey[];
+CHROMEOS_EXPORT extern const char kHasChromeOSKeyboard[];
 CHROMEOS_EXPORT extern const char kHomedir[];
 CHROMEOS_EXPORT extern const char kHostPairingOobe[];
 CHROMEOS_EXPORT extern const char kIgnoreUserProfileMappingForTests[];
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 6fa2b22..343470f 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -22,6 +22,8 @@
     "ble_scanner.h",
     "connect_tethering_operation.cc",
     "connect_tethering_operation.h",
+    "crash_recovery_manager.cc",
+    "crash_recovery_manager.h",
     "device_id_tether_network_guid_map.cc",
     "device_id_tether_network_guid_map.h",
     "device_status_util.cc",
@@ -156,6 +158,7 @@
     "ble_connection_manager_unittest.cc",
     "ble_scanner_unittest.cc",
     "connect_tethering_operation_unittest.cc",
+    "crash_recovery_manager_unittest.cc",
     "device_status_util_unittest.cc",
     "disconnect_tethering_operation_unittest.cc",
     "host_connection_metrics_logger_unittest.cc",
diff --git a/chromeos/components/tether/active_host.h b/chromeos/components/tether/active_host.h
index 10b24cd7..91a04e4 100644
--- a/chromeos/components/tether/active_host.h
+++ b/chromeos/components/tether/active_host.h
@@ -140,6 +140,8 @@
       const std::string& new_wifi_network_guid);
 
  private:
+  friend class CrashRecoveryManager;
+
   void SetActiveHost(ActiveHostStatus active_host_status,
                      const std::string& active_host_device_id,
                      const std::string& tether_network_guid,
diff --git a/chromeos/components/tether/crash_recovery_manager.cc b/chromeos/components/tether/crash_recovery_manager.cc
new file mode 100644
index 0000000..59b0b34
--- /dev/null
+++ b/chromeos/components/tether/crash_recovery_manager.cc
@@ -0,0 +1,151 @@
+// 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 "chromeos/components/tether/crash_recovery_manager.h"
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/components/tether/host_scan_cache.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+CrashRecoveryManager::CrashRecoveryManager(
+    NetworkStateHandler* network_state_handler,
+    ActiveHost* active_host,
+    HostScanCache* host_scan_cache)
+    : network_state_handler_(network_state_handler),
+      active_host_(active_host),
+      host_scan_cache_(host_scan_cache),
+      weak_ptr_factory_(this) {}
+
+CrashRecoveryManager::~CrashRecoveryManager() {}
+
+void CrashRecoveryManager::RestorePreCrashStateIfNecessary(
+    const base::Closure& on_restoration_finished) {
+  ActiveHost::ActiveHostStatus status = active_host_->GetActiveHostStatus();
+  std::string active_host_device_id = active_host_->GetActiveHostDeviceId();
+  std::string wifi_network_guid = active_host_->GetWifiNetworkGuid();
+  std::string tether_network_guid = active_host_->GetTetherNetworkGuid();
+
+  if (status == ActiveHost::ActiveHostStatus::DISCONNECTED) {
+    // There was no active Tether session, so either the last Tether component
+    // shutdown occurred normally (i.e., without a crash), or it occurred due
+    // to a crash and there was no active host at the time of the crash.
+    on_restoration_finished.Run();
+    return;
+  }
+
+  if (status == ActiveHost::ActiveHostStatus::CONNECTING) {
+    // If a connection attempt was in progress when the crash occurred, abandon
+    // the connection attempt. Set ActiveHost back to DISCONNECTED; the user can
+    // attempt another connection if desired.
+    // TODO(khorimoto): Explore whether we should attempt to restore an
+    // in-progress connection attempt. This is a low-priority edge case which is
+    // difficult to solve.
+    PA_LOG(WARNING) << "Browser crashed while Tether connection attempt was in "
+                    << "progress. Abandoning connection attempt.";
+    active_host_->SetActiveHostDisconnected();
+    on_restoration_finished.Run();
+    return;
+  }
+
+  RestoreConnectedState(on_restoration_finished, active_host_device_id,
+                        tether_network_guid, wifi_network_guid);
+}
+
+void CrashRecoveryManager::RestoreConnectedState(
+    const base::Closure& on_restoration_finished,
+    const std::string& active_host_device_id,
+    const std::string& tether_network_guid,
+    const std::string& wifi_network_guid) {
+  // Since the host was connected, both a Wi-Fi and Tether network GUID are
+  // expected to be present.
+  DCHECK(!wifi_network_guid.empty() && !tether_network_guid.empty());
+
+  if (!host_scan_cache_->ExistsInCache(tether_network_guid)) {
+    // If a crash occurred, HostScanCache is expected to have restored its state
+    // via its persistent scan results. If that did not happen correctly, there
+    // is no way to restore the active host, so abandon the connection. Note
+    // that this is not an expected error condition.
+    PA_LOG(ERROR)
+        << "Browser crashed while a Tether connection was active, "
+        << "but the scan result for the active host was lost. Setting "
+        << "the active host to DISCONNECTED.";
+    active_host_->SetActiveHostDisconnected();
+    on_restoration_finished.Run();
+    return;
+  }
+
+  // Since the associated scan result exists in the cache, it is expected to be
+  // present in the network stack.
+  const NetworkState* tether_state =
+      network_state_handler_->GetNetworkStateFromGuid(tether_network_guid);
+  DCHECK(tether_state);
+
+  const NetworkState* wifi_state =
+      network_state_handler_->GetNetworkStateFromGuid(wifi_network_guid);
+  if (!wifi_state || !wifi_state->IsConnectedState()) {
+    // If the Wi-Fi network corresponding to the Tether hotspot is not present
+    // or is no longer connected, then this device is no longer truly connected
+    // to the active host.
+    PA_LOG(ERROR) << "Browser crashed while a Tether connection was active, "
+                  << "but underlying Wi-Fi network corresponding to the Tether "
+                  << "connection is no longer present. Setting the active host "
+                  << "to DISCONNECTED.";
+    active_host_->SetActiveHostDisconnected();
+    on_restoration_finished.Run();
+    return;
+  }
+
+  // Because the NetworkState object representing the Wi-Fi network was lost
+  // during the crash, the association between it and the Tether NetworkState
+  // has been broken. Restore it now.
+  DCHECK(wifi_state->tether_guid().empty());
+  network_state_handler_->AssociateTetherNetworkStateWithWifiNetwork(
+      tether_network_guid, wifi_network_guid);
+
+  active_host_->GetActiveHost(
+      base::Bind(&CrashRecoveryManager::OnActiveHostFetched,
+                 weak_ptr_factory_.GetWeakPtr(), on_restoration_finished));
+}
+
+void CrashRecoveryManager::OnActiveHostFetched(
+    const base::Closure& on_restoration_finished,
+    ActiveHost::ActiveHostStatus active_host_status,
+    std::shared_ptr<cryptauth::RemoteDevice> active_host,
+    const std::string& tether_network_guid,
+    const std::string& wifi_network_guid) {
+  DCHECK(ActiveHost::ActiveHostStatus::CONNECTED == active_host_status);
+  DCHECK(active_host_);
+
+  // Even though the active host has not actually changed, fire an active host
+  // changed update so that classes listening on ActiveHost (e.g.,
+  // ActiveHostNetworkStateUpdater and KeepAliveScheduler) will be notified
+  // that there is an active connection.
+  //
+  // Note: SendActiveHostChangedUpdate() is a protected function of ActiveHost
+  // which is only invoked here because CrashRecoveryManager is a friend class.
+  // It is invoked directly here because we are sending out a "fake" change
+  // event which has equal old and new values.
+  active_host_->SendActiveHostChangedUpdate(
+      ActiveHost::ActiveHostStatus::CONNECTED /* old_status */,
+      active_host->GetDeviceId() /* old_active_host_id */,
+      tether_network_guid /* old_tether_network_guid */,
+      wifi_network_guid /* old_wifi_network_guid */,
+      ActiveHost::ActiveHostStatus::CONNECTED /* new_status */,
+      active_host /* new_active_host */,
+      tether_network_guid /* new_tether_network_guid */,
+      wifi_network_guid /* new_wifi_network_guid */);
+  on_restoration_finished.Run();
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/crash_recovery_manager.h b/chromeos/components/tether/crash_recovery_manager.h
new file mode 100644
index 0000000..998a023
--- /dev/null
+++ b/chromeos/components/tether/crash_recovery_manager.h
@@ -0,0 +1,64 @@
+// 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 CHROMEOS_COMPONENTS_TETHER_CRASH_RECOVERY_MANAGER_H_
+#define CHROMEOS_COMPONENTS_TETHER_CRASH_RECOVERY_MANAGER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chromeos/components/tether/active_host.h"
+
+namespace chromeos {
+
+class NetworkStateHandler;
+
+namespace tether {
+
+class HostScanCache;
+
+// Restores Tether state after a browser crash.
+class CrashRecoveryManager {
+ public:
+  CrashRecoveryManager(NetworkStateHandler* network_state_handler,
+                       ActiveHost* active_host,
+                       HostScanCache* host_scan_cache);
+  virtual ~CrashRecoveryManager();
+
+  // Restores state which was lost by a browser crash. If a crash did not occur
+  // the last time that the Tether component was active, this function is a
+  // no-op. If there was an active Tether connection and the browser crashed,
+  // this function restores the Tether connection.
+  //
+  // This function should only be called during the initialization of the Tether
+  // component.
+  void RestorePreCrashStateIfNecessary(
+      const base::Closure& on_restoration_finished);
+
+ private:
+  void RestoreConnectedState(const base::Closure& on_restoration_finished,
+                             const std::string& active_host_device_id,
+                             const std::string& tether_network_guid,
+                             const std::string& wifi_network_guid);
+  void OnActiveHostFetched(const base::Closure& on_restoration_finished,
+                           ActiveHost::ActiveHostStatus active_host_status,
+                           std::shared_ptr<cryptauth::RemoteDevice> active_host,
+                           const std::string& tether_network_guid,
+                           const std::string& wifi_network_guid);
+
+  NetworkStateHandler* network_state_handler_;
+  ActiveHost* active_host_;
+  HostScanCache* host_scan_cache_;
+
+  base::WeakPtrFactory<CrashRecoveryManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrashRecoveryManager);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_CRASH_RECOVERY_MANAGER_H_
diff --git a/chromeos/components/tether/crash_recovery_manager_unittest.cc b/chromeos/components/tether/crash_recovery_manager_unittest.cc
new file mode 100644
index 0000000..e53e7caf
--- /dev/null
+++ b/chromeos/components/tether/crash_recovery_manager_unittest.cc
@@ -0,0 +1,191 @@
+// 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 "chromeos/components/tether/crash_recovery_manager.h"
+
+#include <memory>
+#include <sstream>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
+#include "chromeos/components/tether/fake_active_host.h"
+#include "chromeos/components/tether/fake_host_scan_cache.h"
+#include "chromeos/components/tether/host_scan_cache_entry.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_test.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+constexpr char kWifiNetworkGuid[] = "wifiNetworkGuid";
+
+std::string CreateConfigurationJsonString(bool is_connected) {
+  std::string connection_state =
+      is_connected ? shill::kStateOnline : shill::kStateIdle;
+
+  std::stringstream ss;
+  ss << "{"
+     << "  \"GUID\": \"" << kWifiNetworkGuid << "\","
+     << "  \"Type\": \"" << shill::kTypeWifi << "\","
+     << "  \"State\": \"" << connection_state << "\""
+     << "}";
+  return ss.str();
+}
+
+}  // namespace
+
+class CrashRecoveryManagerTest : public NetworkStateTest {
+ protected:
+  CrashRecoveryManagerTest()
+      : test_device_(cryptauth::GenerateTestRemoteDevices(1u)[0]) {}
+  ~CrashRecoveryManagerTest() override {}
+
+  void SetUp() override {
+    DBusThreadManager::Initialize();
+    NetworkStateTest::SetUp();
+    network_state_handler()->SetTetherTechnologyState(
+        NetworkStateHandler::TECHNOLOGY_ENABLED);
+
+    is_restoration_finished_ = false;
+
+    fake_active_host_ = base::MakeUnique<FakeActiveHost>();
+    fake_host_scan_cache_ = base::MakeUnique<FakeHostScanCache>();
+
+    crash_recovery_manager_ = base::MakeUnique<CrashRecoveryManager>(
+        network_state_handler(), fake_active_host_.get(),
+        fake_host_scan_cache_.get());
+
+    device_id_tether_network_guid_map_ =
+        base::MakeUnique<DeviceIdTetherNetworkGuidMap>();
+  }
+
+  void TearDown() override {
+    ShutdownNetworkState();
+    NetworkStateTest::TearDown();
+    DBusThreadManager::Shutdown();
+  }
+
+  std::string GetTetherNetworkGuid() {
+    return device_id_tether_network_guid_map_->GetTetherNetworkGuidForDeviceId(
+        test_device_.GetDeviceId());
+  }
+
+  void SetConnected() {
+    fake_active_host_->SetActiveHostConnected(
+        test_device_.GetDeviceId(), GetTetherNetworkGuid(), kWifiNetworkGuid);
+  }
+
+  void AddScanEntry() {
+    fake_host_scan_cache_->SetHostScanResult(
+        *HostScanCacheEntry::Builder()
+             .SetTetherNetworkGuid(GetTetherNetworkGuid())
+             .SetDeviceName("deviceName")
+             .SetCarrier("carrier")
+             .SetBatteryPercentage(100)
+             .SetSignalStrength(100)
+             .SetSetupRequired(false)
+             .Build());
+
+    network_state_handler()->AddTetherNetworkState(
+        GetTetherNetworkGuid(), "deviceName", "carrier",
+        100 /* battery_percentage */, 100 /* signal_strength */,
+        false /* has_connected_to_host */);
+  }
+
+  void AddWifiNetwork(bool is_connected) {
+    ConfigureService(CreateConfigurationJsonString(is_connected));
+  }
+
+  void StartRestoration() {
+    crash_recovery_manager_->RestorePreCrashStateIfNecessary(
+        base::Bind(&CrashRecoveryManagerTest::OnRestorationFinished,
+                   base::Unretained(this)));
+  }
+
+  void OnRestorationFinished() { is_restoration_finished_ = true; }
+
+  void VerifyDisconnectedAfterRestoration() {
+    EXPECT_TRUE(is_restoration_finished_);
+    EXPECT_EQ(ActiveHost::ActiveHostStatus::DISCONNECTED,
+              fake_active_host_->GetActiveHostStatus());
+  }
+
+  const base::test::ScopedTaskEnvironment scoped_task_environment_;
+  const cryptauth::RemoteDevice test_device_;
+
+  std::unique_ptr<FakeActiveHost> fake_active_host_;
+  std::unique_ptr<FakeHostScanCache> fake_host_scan_cache_;
+
+  // TODO(hansberry): Use a fake for this when a real mapping scheme is created.
+  std::unique_ptr<DeviceIdTetherNetworkGuidMap>
+      device_id_tether_network_guid_map_;
+
+  bool is_restoration_finished_;
+
+  std::unique_ptr<CrashRecoveryManager> crash_recovery_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrashRecoveryManagerTest);
+};
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostDisconnected) {
+  StartRestoration();
+  VerifyDisconnectedAfterRestoration();
+}
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostConnecting) {
+  fake_active_host_->SetActiveHostConnecting(test_device_.GetDeviceId(),
+                                             GetTetherNetworkGuid());
+
+  StartRestoration();
+  VerifyDisconnectedAfterRestoration();
+}
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostConnected_EntryNotInCache) {
+  SetConnected();
+
+  StartRestoration();
+  VerifyDisconnectedAfterRestoration();
+}
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostConnected_WifiNetworkMissing) {
+  AddScanEntry();
+  SetConnected();
+
+  StartRestoration();
+  VerifyDisconnectedAfterRestoration();
+}
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostConnected_WifiNetworkDisconnected) {
+  AddScanEntry();
+  AddWifiNetwork(false /* is_connected */);
+  SetConnected();
+
+  StartRestoration();
+  VerifyDisconnectedAfterRestoration();
+}
+
+TEST_F(CrashRecoveryManagerTest, ActiveHostConnected_RestoreSuccessful) {
+  AddScanEntry();
+  AddWifiNetwork(true /* is_connected */);
+  SetConnected();
+
+  StartRestoration();
+  EXPECT_TRUE(is_restoration_finished_);
+  EXPECT_EQ(ActiveHost::ActiveHostStatus::CONNECTED,
+            fake_active_host_->GetActiveHostStatus());
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/initializer.cc b/chromeos/components/tether/initializer.cc
index aec888a9..0fd2f85 100644
--- a/chromeos/components/tether/initializer.cc
+++ b/chromeos/components/tether/initializer.cc
@@ -8,6 +8,7 @@
 #include "chromeos/components/tether/active_host.h"
 #include "chromeos/components/tether/active_host_network_state_updater.h"
 #include "chromeos/components/tether/ble_connection_manager.h"
+#include "chromeos/components/tether/crash_recovery_manager.h"
 #include "chromeos/components/tether/device_id_tether_network_guid_map.h"
 #include "chromeos/components/tether/host_connection_metrics_logger.h"
 #include "chromeos/components/tether/host_scan_device_prioritizer_impl.h"
@@ -248,8 +249,18 @@
           network_connection_handler_, tether_connector_.get(),
           tether_disconnector_.get());
 
-  // Because Initializer is created on each user log in, it's appropriate to
-  // call this method now.
+  crash_recovery_manager_ = base::MakeUnique<CrashRecoveryManager>(
+      network_state_handler_, active_host_.get(),
+      master_host_scan_cache_.get());
+  crash_recovery_manager_->RestorePreCrashStateIfNecessary(base::Bind(
+      &Initializer::OnPreCrashStateRestored, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void Initializer::OnPreCrashStateRestored() {
+  // |crash_recovery_manager_| is no longer needed since it has completed.
+  crash_recovery_manager_.reset();
+
+  // Start a scan now that the Tether module has started up.
   host_scan_scheduler_->UserLoggedIn();
 }
 
diff --git a/chromeos/components/tether/initializer.h b/chromeos/components/tether/initializer.h
index a62cb00..151611c3 100644
--- a/chromeos/components/tether/initializer.h
+++ b/chromeos/components/tether/initializer.h
@@ -37,6 +37,7 @@
 class ActiveHost;
 class ActiveHostNetworkStateUpdater;
 class BleConnectionManager;
+class CrashRecoveryManager;
 class NetworkConnectionHandlerTetherDelegate;
 class DeviceIdTetherNetworkGuidMap;
 class HostScanner;
@@ -103,6 +104,7 @@
       scoped_refptr<device::BluetoothAdapter> adapter);
   void OnBluetoothAdapterAdvertisingIntervalError(
       device::BluetoothAdvertisement::ErrorCode status);
+  void OnPreCrashStateRestored();
 
   cryptauth::CryptAuthService* cryptauth_service_;
   std::unique_ptr<NotificationPresenter> notification_presenter_;
@@ -145,6 +147,7 @@
       network_connection_handler_tether_delegate_;
   std::unique_ptr<TetherNetworkDisconnectionHandler>
       tether_network_disconnection_handler_;
+  std::unique_ptr<CrashRecoveryManager> crash_recovery_manager_;
 
   base::WeakPtrFactory<Initializer> weak_ptr_factory_;
 
diff --git a/chromeos/components/tether/tether_connector.cc b/chromeos/components/tether/tether_connector.cc
index fe64790..01faf42 100644
--- a/chromeos/components/tether/tether_connector.cc
+++ b/chromeos/components/tether/tether_connector.cc
@@ -60,6 +60,7 @@
 
   PA_LOG(INFO) << "Attempting to connect to network with GUID "
                << tether_network_guid << ".";
+  notification_presenter_->RemoveConnectionToHostFailedNotification();
 
   const std::string device_id =
       device_id_tether_network_guid_map_->GetDeviceIdForTetherNetworkGuid(
@@ -244,6 +245,13 @@
 
   host_connection_metrics_logger_->RecordConnectionToHostResult(
       connection_to_host_result);
+
+  if (error_name == NetworkConnectionHandler::kErrorConnectFailed) {
+    // Only show notification if the error is kErrorConnectFailed. Other error
+    // names (e.g., kErrorConnectCanceled) are a result of user interaction and
+    // should not result in any error UI.
+    notification_presenter_->NotifyConnectionToHostFailed();
+  }
 }
 
 void TetherConnector::SetConnectionSucceeded(
diff --git a/chromeos/components/tether/tether_connector_unittest.cc b/chromeos/components/tether/tether_connector_unittest.cc
index d4a2513..d95bde0 100644
--- a/chromeos/components/tether/tether_connector_unittest.cc
+++ b/chromeos/components/tether/tether_connector_unittest.cc
@@ -287,6 +287,8 @@
               fake_active_host_->GetActiveHostStatus());
     EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
               GetResultAndReset());
+    EXPECT_TRUE(fake_notification_presenter_
+                    ->is_connection_failed_notification_shown());
   }
 
   std::string GetResultAndReset() {
@@ -346,6 +348,8 @@
   EXPECT_EQ(ActiveHost::ActiveHostStatus::DISCONNECTED,
             fake_active_host_->GetActiveHostStatus());
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
+  EXPECT_TRUE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest, TestCancelWhileOperationActive) {
@@ -378,6 +382,8 @@
             fake_active_host_->GetActiveHostStatus());
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectCanceled,
             GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest,
@@ -429,6 +435,17 @@
           CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_REQUIRED);
 }
 
+TEST_F(TetherConnectorTest,
+       ConnectionToHostFailedNotificationRemovedWhenConnectionStarts) {
+  // Start with the "connection to host failed" notification showing.
+  fake_notification_presenter_->NotifyConnectionToHostFailed();
+
+  // Starting a connection should result in it being removed.
+  CallConnect(GetTetherNetworkGuid(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
+}
+
 TEST_F(TetherConnectorTest, TestConnectingToWifiFails) {
   EXPECT_CALL(*mock_host_connection_metrics_logger_,
               RecordConnectionToHostResult(
@@ -468,6 +485,8 @@
   EXPECT_EQ(ActiveHost::ActiveHostStatus::DISCONNECTED,
             fake_active_host_->GetActiveHostStatus());
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
+  EXPECT_TRUE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest, TestCancelWhileConnectingToWifi) {
@@ -504,6 +523,8 @@
             fake_active_host_->GetActiveHostStatus());
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectCanceled,
             GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest, TestSuccessfulConnection) {
@@ -553,6 +574,8 @@
   EXPECT_EQ(kWifiNetworkGuid, fake_active_host_->GetWifiNetworkGuid());
 
   EXPECT_EQ(kSuccessResult, GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest, TestSuccessfulConnection_SetupRequired) {
@@ -588,6 +611,8 @@
       fake_notification_presenter_->is_setup_required_notification_shown());
 
   EXPECT_EQ(kSuccessResult, GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 }
 
 TEST_F(TetherConnectorTest,
@@ -607,6 +632,8 @@
   // error.
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectCanceled,
             GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
 
   // Now invoke the callbacks. An operation should have been created for the
   // device 1, not device 0.
@@ -645,6 +672,8 @@
   // error.
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectCanceled,
             GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
   fake_tether_host_fetcher_->InvokePendingCallbacks();
 
   // Now, the active host should be the second device.
@@ -708,6 +737,8 @@
   // error.
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectCanceled,
             GetResultAndReset());
+  EXPECT_FALSE(
+      fake_notification_presenter_->is_connection_failed_notification_shown());
   fake_tether_host_fetcher_->InvokePendingCallbacks();
 
   // Connect successfully to the first Wi-Fi network. Even though a temporary
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
index dd901d8..b74684a 100644
--- a/components/exo/buffer.cc
+++ b/components/exo/buffer.cc
@@ -21,9 +21,9 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/single_release_callback.h"
 #include "components/exo/layer_tree_frame_sink_holder.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/quads/resource_format.h"
 #include "components/viz/common/quads/texture_mailbox.h"
 #include "gpu/command_buffer/client/context_support.h"
@@ -99,9 +99,9 @@
 class Buffer::Texture : public ui::ContextFactoryObserver {
  public:
   Texture(ui::ContextFactory* context_factory,
-          cc::ContextProvider* context_provider);
+          viz::ContextProvider* context_provider);
   Texture(ui::ContextFactory* context_factory,
-          cc::ContextProvider* context_provider,
+          viz::ContextProvider* context_provider,
           gfx::GpuMemoryBuffer* gpu_memory_buffer,
           unsigned texture_target,
           unsigned query_type);
@@ -148,7 +148,7 @@
 
   gfx::GpuMemoryBuffer* const gpu_memory_buffer_;
   ui::ContextFactory* context_factory_;
-  scoped_refptr<cc::ContextProvider> context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
   const unsigned texture_target_;
   const unsigned query_type_;
   const GLenum internalformat_;
@@ -165,7 +165,7 @@
 };
 
 Buffer::Texture::Texture(ui::ContextFactory* context_factory,
-                         cc::ContextProvider* context_provider)
+                         viz::ContextProvider* context_provider)
     : gpu_memory_buffer_(nullptr),
       context_factory_(context_factory),
       context_provider_(context_provider),
@@ -182,7 +182,7 @@
 }
 
 Buffer::Texture::Texture(ui::ContextFactory* context_factory,
-                         cc::ContextProvider* context_provider,
+                         viz::ContextProvider* context_provider,
                          gfx::GpuMemoryBuffer* gpu_memory_buffer,
                          unsigned texture_target,
                          unsigned query_type)
@@ -429,7 +429,7 @@
   ui::ContextFactory* context_factory =
       aura::Env::GetInstance()->context_factory();
   // Note: This can fail if GPU acceleration has been disabled.
-  scoped_refptr<cc::ContextProvider> context_provider =
+  scoped_refptr<viz::ContextProvider> context_provider =
       context_factory->SharedMainThreadContextProvider();
   if (!context_provider) {
     DLOG(WARNING) << "Failed to acquire a context provider";
diff --git a/components/exo/buffer_unittest.cc b/components/exo/buffer_unittest.cc
index d5ce3d2..bf562f98 100644
--- a/components/exo/buffer_unittest.cc
+++ b/components/exo/buffer_unittest.cc
@@ -5,12 +5,12 @@
 #include <GLES2/gl2extchromium.h>
 
 #include "base/bind.h"
-#include "cc/output/context_provider.h"
 #include "cc/resources/single_release_callback.h"
 #include "components/exo/buffer.h"
 #include "components/exo/surface_tree_host.h"
 #include "components/exo/test/exo_test_base.h"
 #include "components/exo/test/exo_test_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/khronos/GLES2/gl2.h"
@@ -81,7 +81,7 @@
                                                 &resource);
   ASSERT_TRUE(rv);
 
-  scoped_refptr<cc::ContextProvider> context_provider =
+  scoped_refptr<viz::ContextProvider> context_provider =
       aura::Env::GetInstance()
           ->context_factory()
           ->SharedMainThreadContextProvider();
diff --git a/components/feedback/BUILD.gn b/components/feedback/BUILD.gn
index 28cae94..e92d5d0 100644
--- a/components/feedback/BUILD.gn
+++ b/components/feedback/BUILD.gn
@@ -24,6 +24,10 @@
     "feedback_uploader_factory.h",
     "feedback_util.cc",
     "feedback_util.h",
+    "system_logs/system_logs_fetcher.cc",
+    "system_logs/system_logs_fetcher.h",
+    "system_logs/system_logs_source.cc",
+    "system_logs/system_logs_source.h",
     "tracing_manager.cc",
     "tracing_manager.h",
   ]
diff --git a/chrome/browser/feedback/system_logs/system_logs_fetcher.cc b/components/feedback/system_logs/system_logs_fetcher.cc
similarity index 94%
rename from chrome/browser/feedback/system_logs/system_logs_fetcher.cc
rename to components/feedback/system_logs/system_logs_fetcher.cc
index 2705636..4e7e510 100644
--- a/chrome/browser/feedback/system_logs/system_logs_fetcher.cc
+++ b/components/feedback/system_logs/system_logs_fetcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 #include <utility>
 
@@ -20,9 +20,7 @@
 // List of keys in the SystemLogsResponse map whose corresponding values will
 // not be anonymized.
 constexpr const char* const kWhitelistedKeysOfUUIDs[] = {
-    "CHROMEOS_BOARD_APPID",
-    "CHROMEOS_CANARY_APPID",
-    "CHROMEOS_RELEASE_APPID",
+    "CHROMEOS_BOARD_APPID", "CHROMEOS_CANARY_APPID", "CHROMEOS_RELEASE_APPID",
     "CLIENT_ID",
 };
 
diff --git a/chrome/browser/feedback/system_logs/system_logs_fetcher.h b/components/feedback/system_logs/system_logs_fetcher.h
similarity index 90%
rename from chrome/browser/feedback/system_logs/system_logs_fetcher.h
rename to components/feedback/system_logs/system_logs_fetcher.h
index 4edb8358..1799d4f 100644
--- a/chrome/browser/feedback/system_logs/system_logs_fetcher.h
+++ b/components/feedback/system_logs/system_logs_fetcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
-#define CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
+#ifndef COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
+#define COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
 
 #include <stddef.h>
 
@@ -15,9 +15,9 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
 #include "components/feedback/anonymizer_tool.h"
 #include "components/feedback/feedback_common.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
@@ -80,4 +80,4 @@
 
 }  // namespace system_logs
 
-#endif  // CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
+#endif  // COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
diff --git a/chrome/browser/feedback/system_logs/system_logs_source.cc b/components/feedback/system_logs/system_logs_source.cc
similarity index 84%
rename from chrome/browser/feedback/system_logs/system_logs_source.cc
rename to components/feedback/system_logs/system_logs_source.cc
index adc9cec..f32cb2f1 100644
--- a/chrome/browser/feedback/system_logs/system_logs_source.cc
+++ b/components/feedback/system_logs/system_logs_source.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/feedback/system_logs/system_logs_source.h"
+#include "components/feedback/system_logs/system_logs_source.h"
 
 namespace system_logs {
 
diff --git a/chrome/browser/feedback/system_logs/system_logs_source.h b/components/feedback/system_logs/system_logs_source.h
similarity index 84%
rename from chrome/browser/feedback/system_logs/system_logs_source.h
rename to components/feedback/system_logs/system_logs_source.h
index 7135721..6b267ef 100644
--- a/chrome/browser/feedback/system_logs/system_logs_source.h
+++ b/components/feedback/system_logs/system_logs_source.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
-#define CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
+#ifndef COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
+#define COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
 
 #include <string>
 
@@ -36,4 +36,4 @@
 
 }  // namespace system_logs
 
-#endif  // CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
+#endif  // COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
diff --git a/components/find_in_page_strings.grdp b/components/find_in_page_strings.grdp
index a1fd5422..fa48a79 100644
--- a/components/find_in_page_strings.grdp
+++ b/components/find_in_page_strings.grdp
@@ -2,7 +2,7 @@
 <grit-part>
 
   <message name="IDS_FIND_IN_PAGE_COUNT" desc="How many matches found and what item we are showing">
-    <ph name="ACTIVE_MATCH">$1<ex>1</ex></ph> of <ph name="TOTAL_MATCHCOUNT">$2<ex>5</ex></ph>
+    <ph name="ACTIVE_MATCH">$1</ph>/<ph name="TOTAL_MATCHCOUNT">$2</ph>
   </message>
   <message name="IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP" desc="The tooltip for the previous button">
     Previous
diff --git a/components/safe_browsing/BUILD.gn b/components/safe_browsing/BUILD.gn
index 7b090bc..f09e7c6 100644
--- a/components/safe_browsing/BUILD.gn
+++ b/components/safe_browsing/BUILD.gn
@@ -28,6 +28,7 @@
     "//base:base",
     "//base:i18n",
     "//components/safe_browsing/common:safe_browsing_prefs",
+    "//components/safe_browsing/web_ui:constants",
     "//components/safe_browsing_db:database_manager",
     "//components/security_interstitials/core:core",
     "//content/public/browser:browser",
diff --git a/components/safe_browsing/base_resource_throttle.cc b/components/safe_browsing/base_resource_throttle.cc
index 63907f2b..8de2554 100644
--- a/components/safe_browsing/base_resource_throttle.cc
+++ b/components/safe_browsing/base_resource_throttle.cc
@@ -10,6 +10,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/web_ui/constants.h"
 #include "components/safe_browsing_db/util.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "content/public/browser/browser_thread.h"
@@ -349,6 +350,10 @@
   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_,
                             content::RESOURCE_TYPE_LAST_TYPE);
 
+  if (CheckWebUIUrls(url)) {
+    return false;
+  }
+
   if (database_manager_->CheckBrowseUrl(url, threat_types_, this)) {
     threat_type_ = SB_THREAT_TYPE_SAFE;
     ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay.
@@ -369,6 +374,28 @@
   return false;
 }
 
+bool BaseResourceThrottle::CheckWebUIUrls(const GURL& url) {
+  DCHECK(threat_type_ == safe_browsing::SB_THREAT_TYPE_SAFE);
+  if (url == kChromeUISafeBrowsingMatchMalwareUrl) {
+    threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+  } else if (url == kChromeUISafeBrowsingMatchPhishingUrl) {
+    threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
+  } else if (url == kChromeUISafeBrowsingMatchUnwantedUrl) {
+    threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_UNWANTED;
+  }
+
+  if (threat_type_ != safe_browsing::SB_THREAT_TYPE_SAFE) {
+    state_ = STATE_CHECKING_URL;
+    url_being_checked_ = url;
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO, FROM_HERE,
+        base::Bind(&BaseResourceThrottle::OnCheckBrowseUrlResult, AsWeakPtr(),
+                   url, threat_type_, ThreatMetadata()));
+    return true;
+  }
+  return false;
+}
+
 void BaseResourceThrottle::OnCheckUrlTimeout() {
   CHECK_EQ(state_, STATE_CHECKING_URL);
 
diff --git a/components/safe_browsing/base_resource_throttle.h b/components/safe_browsing/base_resource_throttle.h
index 2ae5070..249be76f 100644
--- a/components/safe_browsing/base_resource_throttle.h
+++ b/components/safe_browsing/base_resource_throttle.h
@@ -129,6 +129,11 @@
     DEFERRED_PROCESSING,
   };
 
+  // Checks if |url| is one of the hardcoded WebUI match URLs. Returns true if
+  // the URL is one of the hardcoded URLs and will post a task to
+  // OnCheckBrowseUrlResult.
+  bool CheckWebUIUrls(const GURL& url);
+
   scoped_refptr<BaseUIManager> ui_manager_;
 
   // Starts running |url| through the safe browsing check. Returns true if the
diff --git a/components/safe_browsing/web_ui/constants.cc b/components/safe_browsing/web_ui/constants.cc
index 881c1cc..4088770 100644
--- a/components/safe_browsing/web_ui/constants.cc
+++ b/components/safe_browsing/web_ui/constants.cc
@@ -10,5 +10,11 @@
 const char kChromeUISafeBrowsingHost[] = "safe-browsing";
 const char kSbUnderConstruction[] =
     "The safe browsing page is under construction.";
+extern const char kChromeUISafeBrowsingMatchMalwareUrl[] =
+    "chrome://safe-browsing/match?type=malware";
+extern const char kChromeUISafeBrowsingMatchPhishingUrl[] =
+    "chrome://safe-browsing/match?type=phishing";
+extern const char kChromeUISafeBrowsingMatchUnwantedUrl[] =
+    "chrome://safe-browsing/match?type=unwanted";
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/web_ui/constants.h b/components/safe_browsing/web_ui/constants.h
index 6169e065..fc1cf9be 100644
--- a/components/safe_browsing/web_ui/constants.h
+++ b/components/safe_browsing/web_ui/constants.h
@@ -10,6 +10,9 @@
 extern const char kChromeUISafeBrowsingURL[];
 extern const char kChromeUISafeBrowsingHost[];
 extern const char kSbUnderConstruction[];
+extern const char kChromeUISafeBrowsingMatchMalwareUrl[];
+extern const char kChromeUISafeBrowsingMatchPhishingUrl[];
+extern const char kChromeUISafeBrowsingMatchUnwantedUrl[];
 
 }  // namespace safe_browsing
 
diff --git a/components/ssl_config/ssl_config_service_manager_pref.cc b/components/ssl_config/ssl_config_service_manager_pref.cc
index f6aaa9a..62bec0f 100644
--- a/components/ssl_config/ssl_config_service_manager_pref.cc
+++ b/components/ssl_config/ssl_config_service_manager_pref.cc
@@ -213,6 +213,13 @@
     local_state->SetDefaultPrefValue(
         ssl_config::prefs::kSSLVersionMax,
         new base::Value(switches::kSSLVersionTLSv13));
+  } else if (tls13_variant == "record-type") {
+    local_state->SetDefaultPrefValue(
+        ssl_config::prefs::kTLS13Variant,
+        new base::Value(switches::kTLS13VariantRecordTypeExperiment));
+    local_state->SetDefaultPrefValue(
+        ssl_config::prefs::kSSLVersionMax,
+        new base::Value(switches::kSSLVersionTLSv13));
   }
 
   PrefChangeRegistrar::NamedChangeCallback local_state_callback =
diff --git a/components/ssl_config/ssl_config_switches.cc b/components/ssl_config/ssl_config_switches.cc
index 691bb89..68c277c3 100644
--- a/components/ssl_config/ssl_config_switches.cc
+++ b/components/ssl_config/ssl_config_switches.cc
@@ -27,5 +27,6 @@
 const char kTLS13VariantDisabled[] = "disabled";
 const char kTLS13VariantDraft[] = "draft";
 const char kTLS13VariantExperiment[] = "experiment";
+const char kTLS13VariantRecordTypeExperiment[] = "record-type";
 
 }  // namespace switches
diff --git a/components/ssl_config/ssl_config_switches.h b/components/ssl_config/ssl_config_switches.h
index 2d115dd3..8b67424 100644
--- a/components/ssl_config/ssl_config_switches.h
+++ b/components/ssl_config/ssl_config_switches.h
@@ -17,6 +17,7 @@
 extern const char kTLS13VariantDisabled[];
 extern const char kTLS13VariantDraft[];
 extern const char kTLS13VariantExperiment[];
+extern const char kTLS13VariantRecordTypeExperiment[];
 
 }  // namespace switches
 
diff --git a/components/viz/client/client_layer_tree_frame_sink.cc b/components/viz/client/client_layer_tree_frame_sink.cc
index ca6af71..0cfef14 100644
--- a/components/viz/client/client_layer_tree_frame_sink.cc
+++ b/components/viz/client/client_layer_tree_frame_sink.cc
@@ -15,8 +15,8 @@
 namespace viz {
 
 ClientLayerTreeFrameSink::ClientLayerTreeFrameSink(
-    scoped_refptr<cc::ContextProvider> context_provider,
-    scoped_refptr<cc::ContextProvider> worker_context_provider,
+    scoped_refptr<ContextProvider> context_provider,
+    scoped_refptr<ContextProvider> worker_context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     SharedBitmapManager* shared_bitmap_manager,
     std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
diff --git a/components/viz/client/client_layer_tree_frame_sink.h b/components/viz/client/client_layer_tree_frame_sink.h
index 22562c6c..4a709aa 100644
--- a/components/viz/client/client_layer_tree_frame_sink.h
+++ b/components/viz/client/client_layer_tree_frame_sink.h
@@ -8,9 +8,9 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "cc/ipc/compositor_frame_sink.mojom.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink.h"
 #include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -25,8 +25,8 @@
                                  public cc::ExternalBeginFrameSourceClient {
  public:
   ClientLayerTreeFrameSink(
-      scoped_refptr<cc::ContextProvider> context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<ContextProvider> context_provider,
+      scoped_refptr<ContextProvider> worker_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       SharedBitmapManager* shared_bitmap_manager,
       std::unique_ptr<cc::SyntheticBeginFrameSource>
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index b074c74..4681b20 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -5,7 +5,11 @@
 import("//components/viz/viz.gni")
 import("//testing/test.gni")
 
-viz_source_set("common") {
+viz_component("common") {
+  output_name = "viz_common"
+
+  defines = [ "VIZ_COMMON_IMPLEMENTATION" ]
+
   sources = [
     "gl_helper.cc",
     "gl_helper.h",
@@ -13,6 +17,12 @@
     "gl_helper_readback_support.h",
     "gl_helper_scaling.cc",
     "gl_helper_scaling.h",
+    "gpu/context_cache_controller.cc",
+    "gpu/context_cache_controller.h",
+    "gpu/context_provider.cc",
+    "gpu/context_provider.h",
+    "gpu/in_process_context_provider.cc",
+    "gpu/in_process_context_provider.h",
     "hit_test/aggregated_hit_test_region.h",
     "quads/resource_format.h",
     "quads/shared_bitmap.cc",
@@ -44,11 +54,17 @@
     "surfaces/surface_sequence.h",
     "surfaces/surface_sequence_generator.cc",
     "surfaces/surface_sequence_generator.h",
+    "viz_common_export.h",
   ]
 
   deps = [
     "//base",
+    "//gpu",
+    "//gpu/command_buffer/client:gles2_implementation",
     "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/command_buffer/service",
+    "//gpu/ipc:gl_in_process_context",
+    "//gpu/skia_bindings:skia_bindings",
     "//mojo/public/cpp/bindings",
     "//skia",
     "//ui/gfx:color_space",
diff --git a/components/viz/common/DEPS b/components/viz/common/DEPS
index fb3a69c8..4efdc769 100644
--- a/components/viz/common/DEPS
+++ b/components/viz/common/DEPS
@@ -4,6 +4,7 @@
     "+gpu/GLES2",
     "+gpu/command_buffer/client",
     "+gpu/command_buffer/common",
+    "+gpu/command_buffer/service",
     "+gpu/ipc/common",
     "+mojo/public/cpp/bindings",
     "+ui/gfx/geometry",
diff --git a/components/viz/common/gl_helper.h b/components/viz/common/gl_helper.h
index 6e6bed7..931b8549 100644
--- a/components/viz/common/gl_helper.h
+++ b/components/viz/common/gl_helper.h
@@ -10,6 +10,7 @@
 #include "base/atomicops.h"
 #include "base/callback.h"
 #include "base/macros.h"
+#include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -133,7 +134,7 @@
 
 // Provides higher level operations on top of the gpu::gles2::GLES2Interface
 // interfaces.
-class GLHelper {
+class VIZ_COMMON_EXPORT GLHelper {
  public:
   GLHelper(gpu::gles2::GLES2Interface* gl,
            gpu::ContextSupport* context_support);
diff --git a/components/viz/common/gl_helper_scaling.h b/components/viz/common/gl_helper_scaling.h
index 6a5a03f..a37ea91 100644
--- a/components/viz/common/gl_helper_scaling.h
+++ b/components/viz/common/gl_helper_scaling.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "components/viz/common/gl_helper.h"
+#include "components/viz/common/viz_common_export.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -23,7 +24,7 @@
 // Implements GPU texture scaling methods.
 // Note that you should probably not use this class directly.
 // See gl_helper.cc::CreateScaler instead.
-class GLHelperScaling {
+class VIZ_COMMON_EXPORT GLHelperScaling {
  public:
   enum ShaderType {
     SHADER_BILINEAR,
diff --git a/components/viz/common/gpu/DEPS b/components/viz/common/gpu/DEPS
new file mode 100644
index 0000000..945aba3f
--- /dev/null
+++ b/components/viz/common/gpu/DEPS
@@ -0,0 +1,12 @@
+include_rules = [
+  "+cc/output",
+  "+cc/resources",
+  "+gpu/command_buffer",
+  "+gpu/GLES2/gl2extchromium.h",
+  "+gpu/ipc",
+  "+gpu/skia_bindings",
+  "+ui/gfx",
+  "+third_party/khronos/GLES2/gl2.h",
+  "+third_party/khronos/GLES2/gl2ext.h",
+  "+third_party/skia/include/gpu",
+]
diff --git a/cc/output/context_cache_controller.cc b/components/viz/common/gpu/context_cache_controller.cc
similarity index 97%
rename from cc/output/context_cache_controller.cc
rename to components/viz/common/gpu/context_cache_controller.cc
index de178771..d49abb7 100644
--- a/cc/output/context_cache_controller.cc
+++ b/components/viz/common/gpu/context_cache_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/context_cache_controller.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -11,7 +11,7 @@
 #include "gpu/command_buffer/client/context_support.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
-namespace cc {
+namespace viz {
 namespace {
 static const int kIdleCleanupDelaySeconds = 1;
 }  // namespace
@@ -172,4 +172,4 @@
     context_lock_->Release();
 }
 
-}  // namespace cc
+}  // namespace viz
diff --git a/cc/output/context_cache_controller.h b/components/viz/common/gpu/context_cache_controller.h
similarity index 90%
rename from cc/output/context_cache_controller.h
rename to components/viz/common/gpu/context_cache_controller.h
index 614ec0d..feea7f33 100644
--- a/cc/output/context_cache_controller.h
+++ b/components/viz/common/gpu/context_cache_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CC_OUTPUT_CONTEXT_CACHE_CONTROLLER_H_
-#define CC_OUTPUT_CONTEXT_CACHE_CONTROLLER_H_
+#ifndef COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
 
 #include <cstdint>
 #include <memory>
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "cc/cc_export.h"
+#include "components/viz/common/viz_common_export.h"
 
 class GrContext;
 
@@ -24,7 +24,7 @@
 class ContextSupport;
 }
 
-namespace cc {
+namespace viz {
 
 // ContextCacheController manages clearing cached data on ContextProvider when
 // appropriate. Currently, cache clearing is triggered when the Context
@@ -32,9 +32,9 @@
 // ContextProvider may have multiple clients, ContextCacheController tracks
 // visibility and idle status across all clients and only cleans up when
 // appropriate.
-class CC_EXPORT ContextCacheController {
+class VIZ_COMMON_EXPORT ContextCacheController {
  public:
-  class CC_EXPORT ScopedToken {
+  class VIZ_COMMON_EXPORT ScopedToken {
    public:
     ~ScopedToken();
 
@@ -105,6 +105,6 @@
   base::WeakPtrFactory<ContextCacheController> weak_factory_;
 };
 
-}  // namespace cc
+}  // namespace viz
 
-#endif  // CC_OUTPUT_CONTEXT_CACHE_CONTROLLER_H_
+#endif  // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
diff --git a/cc/output/context_provider.cc b/components/viz/common/gpu/context_provider.cc
similarity index 89%
rename from cc/output/context_provider.cc
rename to components/viz/common/gpu/context_provider.cc
index 7842136..b54a430 100644
--- a/cc/output/context_provider.cc
+++ b/components/viz/common/gpu/context_provider.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 
-namespace cc {
+namespace viz {
 
 ContextProvider::ScopedContextLock::ScopedContextLock(
     ContextProvider* context_provider)
@@ -23,4 +23,4 @@
   context_provider_->DetachFromThread();
 }
 
-}  // namespace cc
+}  // namespace viz
diff --git a/cc/output/context_provider.h b/components/viz/common/gpu/context_provider.h
similarity index 85%
rename from cc/output/context_provider.h
rename to components/viz/common/gpu/context_provider.h
index 44c6941..35cc27f 100644
--- a/cc/output/context_provider.h
+++ b/components/viz/common/gpu/context_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CC_OUTPUT_CONTEXT_PROVIDER_H_
-#define CC_OUTPUT_CONTEXT_PROVIDER_H_
+#ifndef COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -11,8 +11,8 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
-#include "cc/cc_export.h"
-#include "cc/output/context_cache_controller.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
+#include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/common/capabilities.h"
 
 class GrContext;
@@ -23,19 +23,22 @@
 
 namespace gpu {
 class ContextSupport;
-namespace gles2 { class GLES2Interface; }
+namespace gles2 {
+class GLES2Interface;
 }
+}  // namespace gpu
 
-namespace cc {
+namespace viz {
 
-class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
+class VIZ_COMMON_EXPORT ContextProvider
+    : public base::RefCountedThreadSafe<ContextProvider> {
  public:
   // Hold an instance of this lock while using a context across multiple
   // threads. This only works for ContextProviders that will return a valid
   // lock from GetLock(), so is not always supported. Most use of
   // ContextProvider should be single-thread only on the thread that
   // BindToCurrentThread is run on.
-  class CC_EXPORT ScopedContextLock {
+  class VIZ_COMMON_EXPORT ScopedContextLock {
    public:
     explicit ScopedContextLock(ContextProvider* context_provider);
     ~ScopedContextLock();
@@ -96,6 +99,6 @@
   virtual ~ContextProvider() {}
 };
 
-}  // namespace cc
+}  // namespace viz
 
-#endif  // CC_OUTPUT_CONTEXT_PROVIDER_H_
+#endif  // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
diff --git a/cc/output/in_process_context_provider.cc b/components/viz/common/gpu/in_process_context_provider.cc
similarity index 97%
rename from cc/output/in_process_context_provider.cc
rename to components/viz/common/gpu/in_process_context_provider.cc
index cd53faa..bd20e254 100644
--- a/cc/output/in_process_context_provider.cc
+++ b/components/viz/common/gpu/in_process_context_provider.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/in_process_context_provider.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
 
 #include <stdint.h>
 
@@ -31,7 +31,7 @@
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace cc {
+namespace viz {
 
 namespace {
 
@@ -151,4 +151,4 @@
   context_->SetUpdateVSyncParametersCallback(callback);
 }
 
-}  // namespace cc
+}  // namespace viz
diff --git a/cc/output/in_process_context_provider.h b/components/viz/common/gpu/in_process_context_provider.h
similarity index 81%
rename from cc/output/in_process_context_provider.h
rename to components/viz/common/gpu/in_process_context_provider.h
index 0a355d2..493191f 100644
--- a/cc/output/in_process_context_provider.h
+++ b/components/viz/common/gpu/in_process_context_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CC_OUTPUT_IN_PROCESS_CONTEXT_PROVIDER_H_
-#define CC_OUTPUT_IN_PROCESS_CONTEXT_PROVIDER_H_
+#ifndef COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
 
 #include <stdint.h>
 
@@ -11,9 +11,9 @@
 
 #include "base/compiler_specific.h"
 #include "base/synchronization/lock.h"
-#include "cc/cc_export.h"
-#include "cc/output/context_cache_controller.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/ipc/in_process_command_buffer.h"
 #include "ui/gfx/native_widget_types.h"
@@ -25,15 +25,15 @@
 class GpuMemoryBufferManager;
 class ImageFactory;
 struct SharedMemoryLimits;
-}
+}  // namespace gpu
 
 namespace skia_bindings {
 class GrContextForGLES2Interface;
 }
 
-namespace cc {
+namespace viz {
 
-class CC_EXPORT InProcessContextProvider
+class VIZ_COMMON_EXPORT InProcessContextProvider
     : public NON_EXPORTED_BASE(ContextProvider) {
  public:
   InProcessContextProvider(
@@ -78,6 +78,6 @@
   std::unique_ptr<ContextCacheController> cache_controller_;
 };
 
-}  // namespace cc
+}  // namespace viz
 
-#endif  // CC_OUTPUT_IN_PROCESS_CONTEXT_PROVIDER_H_
+#endif  // COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
diff --git a/components/viz/common/quads/shared_bitmap.h b/components/viz/common/quads/shared_bitmap.h
index 98e9cca..0d5fa83 100644
--- a/components/viz/common/quads/shared_bitmap.h
+++ b/components/viz/common/quads/shared_bitmap.h
@@ -11,6 +11,7 @@
 #include "base/hash.h"
 #include "base/macros.h"
 #include "base/trace_event/memory_allocator_dump.h"
+#include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -27,10 +28,10 @@
   }
 };
 
-base::trace_event::MemoryAllocatorDumpGuid GetSharedBitmapGUIDForTracing(
-    const SharedBitmapId& bitmap_id);
+VIZ_COMMON_EXPORT base::trace_event::MemoryAllocatorDumpGuid
+GetSharedBitmapGUIDForTracing(const SharedBitmapId& bitmap_id);
 
-class SharedBitmap {
+class VIZ_COMMON_EXPORT SharedBitmap {
  public:
   SharedBitmap(uint8_t* pixels,
                const SharedBitmapId& id,
diff --git a/components/viz/common/quads/texture_mailbox.h b/components/viz/common/quads/texture_mailbox.h
index 7136d7e..3c24f3b 100644
--- a/components/viz/common/quads/texture_mailbox.h
+++ b/components/viz/common/quads/texture_mailbox.h
@@ -12,6 +12,7 @@
 
 #include "base/memory/shared_memory.h"
 #include "build/build_config.h"
+#include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "ui/gfx/color_space.h"
@@ -29,7 +30,7 @@
 
 // TODO(skaslev, danakj) Rename this class more apropriately since now it
 // can hold a shared memory resource as well as a texture mailbox.
-class TextureMailbox {
+class VIZ_COMMON_EXPORT TextureMailbox {
  public:
   TextureMailbox();
   TextureMailbox(const TextureMailbox& other);
diff --git a/components/viz/common/resources/buffer_to_texture_target_map.h b/components/viz/common/resources/buffer_to_texture_target_map.h
index b8b7364..27539475 100644
--- a/components/viz/common/resources/buffer_to_texture_target_map.h
+++ b/components/viz/common/resources/buffer_to_texture_target_map.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "components/viz/common/viz_common_export.h"
 #include "ui/gfx/buffer_types.h"
 
 namespace viz {
@@ -18,17 +19,18 @@
 // Converts a serialized ImageTextureTargetsMap back to the runtime format.
 // Serialization takes the form:
 //   "usage,format,target;usage,format,target;...;usage,format,target"
-BufferToTextureTargetMap StringToBufferToTextureTargetMap(
-    const std::string& str);
+VIZ_COMMON_EXPORT BufferToTextureTargetMap
+StringToBufferToTextureTargetMap(const std::string& str);
 
 // Converts an ImageTextureTargetsMap to a string representation of the format:
 //   "usage,format,target;usage,format,target;...;usage,format,target"
-std::string BufferToTextureTargetMapToString(
+VIZ_COMMON_EXPORT std::string BufferToTextureTargetMapToString(
     const BufferToTextureTargetMap& map);
 
 // Returns a default-initialized BufferToTextureTargetsMap where every entry
 // maps to GL_TEXTURE_2D.
-BufferToTextureTargetMap DefaultBufferToTextureTargetMapForTesting();
+VIZ_COMMON_EXPORT BufferToTextureTargetMap
+DefaultBufferToTextureTargetMapForTesting();
 
 }  // namespace viz
 
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index 923357e..935ccc8 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -6,19 +6,21 @@
 #define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
 
 #include "components/viz/common/quads/resource_format.h"
+#include "components/viz/common/viz_common_export.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 
 namespace viz {
 
-SkColorType ResourceFormatToClosestSkColorType(ResourceFormat format);
-int BitsPerPixel(ResourceFormat format);
-GLenum GLDataType(ResourceFormat format);
-GLenum GLDataFormat(ResourceFormat format);
-GLenum GLInternalFormat(ResourceFormat format);
-GLenum GLCopyTextureInternalFormat(ResourceFormat format);
-gfx::BufferFormat BufferFormat(ResourceFormat format);
-bool IsResourceFormatCompressed(ResourceFormat format);
-bool DoesResourceFormatSupportAlpha(ResourceFormat format);
+VIZ_COMMON_EXPORT SkColorType
+ResourceFormatToClosestSkColorType(ResourceFormat format);
+VIZ_COMMON_EXPORT int BitsPerPixel(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLDataType(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLDataFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLInternalFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLCopyTextureInternalFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT gfx::BufferFormat BufferFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT bool IsResourceFormatCompressed(ResourceFormat format);
+VIZ_COMMON_EXPORT bool DoesResourceFormatSupportAlpha(ResourceFormat format);
 
 }  // namespace viz
 
diff --git a/components/viz/common/resources/resource_settings.h b/components/viz/common/resources/resource_settings.h
index e20fe8f..31ab9d0 100644
--- a/components/viz/common/resources/resource_settings.h
+++ b/components/viz/common/resources/resource_settings.h
@@ -6,12 +6,13 @@
 #define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_
 
 #include "components/viz/common/resources/buffer_to_texture_target_map.h"
+#include "components/viz/common/viz_common_export.h"
 
 namespace viz {
 
 // ResourceSettings contains all the settings that are needed to create a
 // ResourceProvider.
-class ResourceSettings {
+class VIZ_COMMON_EXPORT ResourceSettings {
  public:
   ResourceSettings();
   ResourceSettings(const ResourceSettings& other);
diff --git a/components/viz/common/surfaces/frame_sink_id.h b/components/viz/common/surfaces/frame_sink_id.h
index 8657f1e..30319d7b 100644
--- a/components/viz/common/surfaces/frame_sink_id.h
+++ b/components/viz/common/surfaces/frame_sink_id.h
@@ -12,10 +12,11 @@
 #include <tuple>
 
 #include "base/hash.h"
+#include "components/viz/common/viz_common_export.h"
 
 namespace viz {
 
-class FrameSinkId {
+class VIZ_COMMON_EXPORT FrameSinkId {
  public:
   constexpr FrameSinkId() : client_id_(0), sink_id_(0) {}
 
@@ -51,7 +52,8 @@
   uint32_t sink_id_;
 };
 
-std::ostream& operator<<(std::ostream& out, const FrameSinkId& frame_sink_id);
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+                                           const FrameSinkId& frame_sink_id);
 
 struct FrameSinkIdHash {
   size_t operator()(const FrameSinkId& key) const { return key.hash(); }
diff --git a/components/viz/common/surfaces/local_surface_id.h b/components/viz/common/surfaces/local_surface_id.h
index 43bd9b7..450386c6 100644
--- a/components/viz/common/surfaces/local_surface_id.h
+++ b/components/viz/common/surfaces/local_surface_id.h
@@ -13,6 +13,7 @@
 
 #include "base/hash.h"
 #include "base/unguessable_token.h"
+#include "components/viz/common/viz_common_export.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace cc {
@@ -23,7 +24,7 @@
 
 namespace viz {
 
-class LocalSurfaceId {
+class VIZ_COMMON_EXPORT LocalSurfaceId {
  public:
   constexpr LocalSurfaceId() : local_id_(0) {}
 
@@ -71,8 +72,9 @@
   base::UnguessableToken nonce_;
 };
 
-std::ostream& operator<<(std::ostream& out,
-                         const LocalSurfaceId& local_surface_id);
+VIZ_COMMON_EXPORT std::ostream& operator<<(
+    std::ostream& out,
+    const LocalSurfaceId& local_surface_id);
 
 struct LocalSurfaceIdHash {
   size_t operator()(const LocalSurfaceId& key) const { return key.hash(); }
diff --git a/components/viz/common/surfaces/local_surface_id_allocator.h b/components/viz/common/surfaces/local_surface_id_allocator.h
index a1034939..bc01fd5 100644
--- a/components/viz/common/surfaces/local_surface_id_allocator.h
+++ b/components/viz/common/surfaces/local_surface_id_allocator.h
@@ -9,13 +9,14 @@
 
 #include "base/macros.h"
 #include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/viz_common_export.h"
 
 namespace viz {
 
 // This is a helper class for generating local surface IDs for a single
 // FrameSink. This is not threadsafe, to use from multiple threads wrap this
 // class in a mutex.
-class LocalSurfaceIdAllocator {
+class VIZ_COMMON_EXPORT LocalSurfaceIdAllocator {
  public:
   LocalSurfaceIdAllocator();
   ~LocalSurfaceIdAllocator();
diff --git a/components/viz/common/surfaces/sequence_surface_reference_factory.h b/components/viz/common/surfaces/sequence_surface_reference_factory.h
index 9c2096b..93fa953 100644
--- a/components/viz/common/surfaces/sequence_surface_reference_factory.h
+++ b/components/viz/common/surfaces/sequence_surface_reference_factory.h
@@ -7,11 +7,12 @@
 
 #include "components/viz/common/surfaces/surface_reference_factory.h"
 #include "components/viz/common/surfaces/surface_sequence.h"
+#include "components/viz/common/viz_common_export.h"
 
 namespace viz {
 
 // A surface reference factory that uses SurfaceSequence.
-class SequenceSurfaceReferenceFactory
+class VIZ_COMMON_EXPORT SequenceSurfaceReferenceFactory
     : public NON_EXPORTED_BASE(SurfaceReferenceFactory) {
  public:
   SequenceSurfaceReferenceFactory() = default;
diff --git a/components/viz/common/surfaces/surface_id.h b/components/viz/common/surfaces/surface_id.h
index ae5d4731..b008aba 100644
--- a/components/viz/common/surfaces/surface_id.h
+++ b/components/viz/common/surfaces/surface_id.h
@@ -14,6 +14,7 @@
 #include "base/hash.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/viz_common_export.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace cc {
@@ -24,7 +25,7 @@
 
 namespace viz {
 
-class SurfaceId {
+class VIZ_COMMON_EXPORT SurfaceId {
  public:
   constexpr SurfaceId() = default;
 
@@ -75,7 +76,8 @@
   LocalSurfaceId local_surface_id_;
 };
 
-std::ostream& operator<<(std::ostream& out, const SurfaceId& surface_id);
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+                                           const SurfaceId& surface_id);
 
 struct SurfaceIdHash {
   size_t operator()(const SurfaceId& key) const { return key.hash(); }
diff --git a/components/viz/common/surfaces/surface_sequence_generator.h b/components/viz/common/surfaces/surface_sequence_generator.h
index c9e104b..1bed7d5 100644
--- a/components/viz/common/surfaces/surface_sequence_generator.h
+++ b/components/viz/common/surfaces/surface_sequence_generator.h
@@ -13,11 +13,12 @@
 
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/surface_sequence.h"
+#include "components/viz/common/viz_common_export.h"
 
 namespace viz {
 
 // Generates unique surface sequences for a surface client id.
-class SurfaceSequenceGenerator {
+class VIZ_COMMON_EXPORT SurfaceSequenceGenerator {
  public:
   SurfaceSequenceGenerator();
   ~SurfaceSequenceGenerator();
diff --git a/components/viz/common/viz_common_export.h b/components/viz/common/viz_common_export.h
new file mode 100644
index 0000000..9e58b13
--- /dev/null
+++ b/components/viz/common/viz_common_export.h
@@ -0,0 +1,29 @@
+// 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_VIZ_COMMON_VIZ_COMMON_EXPORT_H_
+#define COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VIZ_COMMON_IMPLEMENTATION)
+#define VIZ_COMMON_EXPORT __declspec(dllexport)
+#else
+#define VIZ_COMMON_EXPORT __declspec(dllimport)
+#endif  // defined(VIZ_COMMON_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(VIZ_COMMON_IMPLEMENTATION)
+#define VIZ_COMMON_EXPORT __attribute__((visibility("default")))
+#else
+#define VIZ_COMMON_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define VIZ_COMMON_EXPORT
+#endif
+
+#endif  // COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index e15a3d7..c65121a0 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -138,6 +138,7 @@
     "frame_sinks/direct_layer_tree_frame_sink_unittest.cc",
     "frame_sinks/frame_sink_manager_unittest.cc",
     "frame_sinks/referenced_surface_tracker_unittest.cc",
+    "frame_sinks/surface_references_unittest.cc",
     "frame_sinks/surface_synchronization_unittest.cc",
     "hit_test/hit_test_aggregator_unittest.cc",
   ]
diff --git a/components/viz/service/display_embedder/display_output_surface.cc b/components/viz/service/display_embedder/display_output_surface.cc
index 04cf8f1..23905232 100644
--- a/components/viz/service/display_embedder/display_output_surface.cc
+++ b/components/viz/service/display_embedder/display_output_surface.cc
@@ -9,17 +9,17 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/output_surface_frame.h"
 #include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 
 namespace viz {
 
 DisplayOutputSurface::DisplayOutputSurface(
-    scoped_refptr<cc::InProcessContextProvider> context_provider,
+    scoped_refptr<InProcessContextProvider> context_provider,
     cc::SyntheticBeginFrameSource* synthetic_begin_frame_source)
     : cc::OutputSurface(context_provider),
       synthetic_begin_frame_source_(synthetic_begin_frame_source),
@@ -94,7 +94,7 @@
 
 uint32_t DisplayOutputSurface::GetFramebufferCopyTextureFormat() {
   // TODO(danakj): What attributes are used for the default framebuffer here?
-  // Can it have alpha? cc::InProcessContextProvider doesn't take any
+  // Can it have alpha? InProcessContextProvider doesn't take any
   // attributes.
   return GL_RGB;
 }
diff --git a/components/viz/service/display_embedder/display_output_surface.h b/components/viz/service/display_embedder/display_output_surface.h
index 0dad061e..032db0fa 100644
--- a/components/viz/service/display_embedder/display_output_surface.h
+++ b/components/viz/service/display_embedder/display_output_surface.h
@@ -7,8 +7,8 @@
 
 #include <memory>
 
-#include "cc/output/in_process_context_provider.h"
 #include "cc/output/output_surface.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
 #include "ui/latency/latency_tracker.h"
 
 namespace cc {
@@ -22,7 +22,7 @@
 class DisplayOutputSurface : public cc::OutputSurface {
  public:
   DisplayOutputSurface(
-      scoped_refptr<cc::InProcessContextProvider> context_provider,
+      scoped_refptr<InProcessContextProvider> context_provider,
       cc::SyntheticBeginFrameSource* synthetic_begin_frame_source);
   ~DisplayOutputSurface() override;
 
diff --git a/components/viz/service/display_embedder/display_output_surface_ozone.cc b/components/viz/service/display_embedder/display_output_surface_ozone.cc
index a9d2dc5..1558ad2b 100644
--- a/components/viz/service/display_embedder/display_output_surface_ozone.cc
+++ b/components/viz/service/display_embedder/display_output_surface_ozone.cc
@@ -8,10 +8,10 @@
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/output_surface_frame.h"
 #include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/service/display_embedder/buffer_queue.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -20,7 +20,7 @@
 namespace viz {
 
 DisplayOutputSurfaceOzone::DisplayOutputSurfaceOzone(
-    scoped_refptr<cc::InProcessContextProvider> context_provider,
+    scoped_refptr<InProcessContextProvider> context_provider,
     gfx::AcceleratedWidget widget,
     cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
diff --git a/components/viz/service/display_embedder/display_output_surface_ozone.h b/components/viz/service/display_embedder/display_output_surface_ozone.h
index 46999009..3715e39d 100644
--- a/components/viz/service/display_embedder/display_output_surface_ozone.h
+++ b/components/viz/service/display_embedder/display_output_surface_ozone.h
@@ -8,10 +8,10 @@
 #include <memory>
 
 #include "base/memory/weak_ptr.h"
-#include "cc/output/context_provider.h"
-#include "cc/output/in_process_context_provider.h"
 #include "cc/output/output_surface.h"
 #include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
 #include "components/viz/service/display_embedder/display_output_surface.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
@@ -37,7 +37,7 @@
 class DisplayOutputSurfaceOzone : public DisplayOutputSurface {
  public:
   DisplayOutputSurfaceOzone(
-      scoped_refptr<cc::InProcessContextProvider> context_provider,
+      scoped_refptr<InProcessContextProvider> context_provider,
       gfx::AcceleratedWidget widget,
       cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 0891a9f..df1cb39 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -10,9 +10,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "cc/base/switches.h"
-#include "cc/output/in_process_context_provider.h"
 #include "cc/output/texture_mailbox_deleter.h"
 #include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
 #include "components/viz/service/display/display.h"
 #include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display_embedder/display_output_surface.h"
@@ -59,11 +59,11 @@
       base::MakeUnique<cc::DelayBasedBeginFrameSource>(
           base::MakeUnique<cc::DelayBasedTimeSource>(task_runner_.get()));
 
-  scoped_refptr<cc::InProcessContextProvider> context_provider =
-      new cc::InProcessContextProvider(
-          gpu_service_, surface_handle, gpu_memory_buffer_manager_.get(),
-          image_factory_, gpu::SharedMemoryLimits(),
-          nullptr /* shared_context */);
+  scoped_refptr<InProcessContextProvider> context_provider =
+      new InProcessContextProvider(gpu_service_, surface_handle,
+                                   gpu_memory_buffer_manager_.get(),
+                                   image_factory_, gpu::SharedMemoryLimits(),
+                                   nullptr /* shared_context */);
 
   // TODO(rjkroege): If there is something better to do than CHECK, add it.
   CHECK(context_provider->BindToCurrentThread());
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 7863532..10be06a 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -53,7 +53,7 @@
   EvictCurrentSurface();
   frame_sink_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
   if (handles_frame_sink_id_invalidation_)
-    frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_);
+    surface_manager_->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 void CompositorFrameSinkSupport::SetDestructionCallback(
@@ -318,7 +318,7 @@
   frame_sink_manager_ = frame_sink_manager;
   surface_manager_ = frame_sink_manager->surface_manager();
   if (handles_frame_sink_id_invalidation_)
-    frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_);
+    surface_manager_->RegisterFrameSinkId(frame_sink_id_);
   frame_sink_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
 }
 
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 52f64483..71475e5 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -586,7 +586,8 @@
             local_surface_id);
   local_surface_id_ = LocalSurfaceId();
 
-  manager_.RegisterFrameSinkId(kYetAnotherArbitraryFrameSinkId);
+  manager_.surface_manager()->RegisterFrameSinkId(
+      kYetAnotherArbitraryFrameSinkId);
 
   SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id);
   cc::Surface* surface = GetSurfaceForId(surface_id);
@@ -648,7 +649,7 @@
   support_->SubmitCompositorFrame(local_surface_id,
                                   cc::test::MakeCompositorFrame());
 
-  manager_.RegisterFrameSinkId(frame_sink_id);
+  manager_.surface_manager()->RegisterFrameSinkId(frame_sink_id);
   GetSurfaceForId(id)->AddDestructionDependency(
       SurfaceSequence(frame_sink_id, 4));
 
@@ -657,7 +658,7 @@
   // Verify the dependency has prevented the surface from getting destroyed.
   EXPECT_TRUE(GetSurfaceForId(id));
 
-  manager_.InvalidateFrameSinkId(frame_sink_id);
+  manager_.surface_manager()->InvalidateFrameSinkId(frame_sink_id);
 
   // Verify that the invalidated namespace caused the unsatisfied sequence
   // to be ignored.
@@ -670,7 +671,7 @@
   auto support2 = CompositorFrameSinkSupport::Create(
       &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId,
       kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
-  manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId);
+  manager_.surface_manager()->RegisterFrameSinkId(kAnotherArbitraryFrameSinkId);
   // Give local_surface_id_ an initial frame so another client can refer to
   // that surface.
   {
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
index 4c6b389..4cb312e8 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -21,8 +21,8 @@
     CompositorFrameSinkSupportManager* support_manager,
     FrameSinkManager* frame_sink_manager,
     Display* display,
-    scoped_refptr<cc::ContextProvider> context_provider,
-    scoped_refptr<cc::ContextProvider> worker_context_provider,
+    scoped_refptr<ContextProvider> context_provider,
+    scoped_refptr<ContextProvider> worker_context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     SharedBitmapManager* shared_bitmap_manager)
     : LayerTreeFrameSink(std::move(context_provider),
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
index 30391ff..e132436c 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
@@ -39,8 +39,8 @@
       CompositorFrameSinkSupportManager* support_manager,
       FrameSinkManager* frame_sink_manager,
       Display* display,
-      scoped_refptr<cc::ContextProvider> context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<ContextProvider> context_provider,
+      scoped_refptr<ContextProvider> worker_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       SharedBitmapManager* shared_bitmap_manager);
   DirectLayerTreeFrameSink(
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
index fc5fd11d..0769d88 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
@@ -73,8 +73,6 @@
         display_rect_(display_size_),
         support_manager_(&frame_sink_manager_),
         context_provider_(cc::TestContextProvider::Create()) {
-    frame_sink_manager_.RegisterFrameSinkId(kArbitraryFrameSinkId);
-
     auto display_output_surface = cc::FakeOutputSurface::Create3d();
     display_output_surface_ = display_output_surface.get();
 
diff --git a/components/viz/service/frame_sinks/frame_sink_manager.cc b/components/viz/service/frame_sinks/frame_sink_manager.cc
index bed468a..41683c9 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager.cc
@@ -34,14 +34,6 @@
   DCHECK_EQ(registered_sources_.size(), 0u);
 }
 
-void FrameSinkManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id) {
-  surface_manager_.RegisterFrameSinkId(frame_sink_id);
-}
-
-void FrameSinkManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
-  surface_manager_.InvalidateFrameSinkId(frame_sink_id);
-}
-
 void FrameSinkManager::RegisterFrameSinkManagerClient(
     const FrameSinkId& frame_sink_id,
     FrameSinkManagerClient* client) {
@@ -237,8 +229,4 @@
     RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first);
 }
 
-void FrameSinkManager::DropTemporaryReference(const SurfaceId& surface_id) {
-  surface_manager_.DropTemporaryReference(surface_id);
-}
-
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/frame_sink_manager.h b/components/viz/service/frame_sinks/frame_sink_manager.h
index 18a0700..716d955 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager.h
@@ -41,12 +41,6 @@
                                 cc::SurfaceManager::LifetimeType::SEQUENCES);
   ~FrameSinkManager();
 
-  void RegisterFrameSinkId(const FrameSinkId& frame_sink_id);
-
-  // Invalidate a frame_sink_id that might still have associated sequences,
-  // possibly because a renderer process has crashed.
-  void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id);
-
   // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be
   // registered and unregistered in any order with respect to each other.
   //
@@ -82,11 +76,6 @@
   void UnregisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
                                     const FrameSinkId& child_frame_sink_id);
 
-  // Drops the temporary reference for |surface_id|. If a surface reference has
-  // already been added from the parent to |surface_id| then this will do
-  // nothing.
-  void DropTemporaryReference(const SurfaceId& surface_id);
-
   cc::SurfaceManager* surface_manager() { return &surface_manager_; }
 
  private:
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 795212fd..3ee3655 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -105,7 +105,7 @@
 }
 
 void FrameSinkManagerImpl::DropTemporaryReference(const SurfaceId& surface_id) {
-  manager_.DropTemporaryReference(surface_id);
+  manager_.surface_manager()->DropTemporaryReference(surface_id);
 }
 
 void FrameSinkManagerImpl::DestroyCompositorFrameSink(FrameSinkId sink_id) {
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
index 977351c..b8a7ebf4 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -66,20 +66,9 @@
 
 class FrameSinkManagerTest : public testing::Test {
  public:
-  // These tests don't care about namespace registration, so just preregister
-  // a set of namespaces that tests can use freely without worrying if they're
-  // valid or not.
-  enum { MAX_FRAME_SINK = 10 };
+  FrameSinkManagerTest() = default;
 
-  FrameSinkManagerTest() {
-    for (size_t i = 0; i < MAX_FRAME_SINK; ++i)
-      manager_.RegisterFrameSinkId(FrameSinkId(i, i));
-  }
-
-  ~FrameSinkManagerTest() override {
-    for (size_t i = 0; i < MAX_FRAME_SINK; ++i)
-      manager_.InvalidateFrameSinkId(FrameSinkId(i, i));
-  }
+  ~FrameSinkManagerTest() override = default;
 
  protected:
   FrameSinkManager manager_;
diff --git a/cc/surfaces/surface_manager_ref_unittest.cc b/components/viz/service/frame_sinks/surface_references_unittest.cc
similarity index 72%
rename from cc/surfaces/surface_manager_ref_unittest.cc
rename to components/viz/service/frame_sinks/surface_references_unittest.cc
index 58972b0..dff1b14c 100644
--- a/cc/surfaces/surface_manager_ref_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_references_unittest.cc
@@ -23,86 +23,89 @@
 using testing::SizeIs;
 using testing::UnorderedElementsAre;
 
-namespace cc {
+namespace viz {
+namespace test {
 namespace {
 
-constexpr viz::FrameSinkId kFrameSink1(1, 0);
-constexpr viz::FrameSinkId kFrameSink2(2, 0);
-constexpr viz::FrameSinkId kFrameSink3(3, 0);
+constexpr FrameSinkId kFrameSink1(1, 0);
+constexpr FrameSinkId kFrameSink2(2, 0);
+constexpr FrameSinkId kFrameSink3(3, 0);
 
 }  // namespace
 
-// Tests for reference tracking in SurfaceManager.
-class SurfaceManagerRefTest : public testing::Test {
+// Tests for reference tracking in CompositorFrameSinkSupport and
+// cc::SurfaceManager.
+class SurfaceReferencesTest : public testing::Test {
  public:
-  SurfaceManager& GetSurfaceManager() { return *manager_->surface_manager(); }
+  cc::SurfaceManager& GetSurfaceManager() {
+    return *manager_->surface_manager();
+  }
 
   // Creates a new Surface with the provided |frame_sink_id| and |local_id|.
   // Will first create a Surfacesupport for |frame_sink_id| if necessary.
-  viz::SurfaceId CreateSurface(const viz::FrameSinkId& frame_sink_id,
-                               uint32_t local_id) {
-    viz::LocalSurfaceId local_surface_id(
-        local_id, base::UnguessableToken::Deserialize(0, 1u));
+  SurfaceId CreateSurface(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+    LocalSurfaceId local_surface_id(local_id,
+                                    base::UnguessableToken::Deserialize(0, 1u));
     GetCompositorFrameSinkSupport(frame_sink_id)
-        .SubmitCompositorFrame(local_surface_id, test::MakeCompositorFrame());
-    return viz::SurfaceId(frame_sink_id, local_surface_id);
+        .SubmitCompositorFrame(local_surface_id,
+                               cc::test::MakeCompositorFrame());
+    return SurfaceId(frame_sink_id, local_surface_id);
   }
 
   // Destroy Surface with |surface_id|.
-  void DestroySurface(const viz::SurfaceId& surface_id) {
+  void DestroySurface(const SurfaceId& surface_id) {
     GetCompositorFrameSinkSupport(surface_id.frame_sink_id())
         .EvictCurrentSurface();
   }
 
-  viz::CompositorFrameSinkSupport& GetCompositorFrameSinkSupport(
-      const viz::FrameSinkId& frame_sink_id) {
+  CompositorFrameSinkSupport& GetCompositorFrameSinkSupport(
+      const FrameSinkId& frame_sink_id) {
     auto& support_ptr = supports_[frame_sink_id];
     if (!support_ptr) {
       constexpr bool is_root = false;
       constexpr bool handles_frame_sink_id_invalidation = true;
       constexpr bool needs_sync_points = true;
-      support_ptr = viz::CompositorFrameSinkSupport::Create(
+      support_ptr = CompositorFrameSinkSupport::Create(
           nullptr, manager_.get(), frame_sink_id, is_root,
           handles_frame_sink_id_invalidation, needs_sync_points);
     }
     return *support_ptr;
   }
 
-  void DestroyCompositorFrameSinkSupport(
-      const viz::FrameSinkId& frame_sink_id) {
+  void DestroyCompositorFrameSinkSupport(const FrameSinkId& frame_sink_id) {
     auto support_ptr = supports_.find(frame_sink_id);
     ASSERT_NE(support_ptr, supports_.end());
     supports_.erase(support_ptr);
   }
 
-  void RemoveSurfaceReference(const viz::SurfaceId& parent_id,
-                              const viz::SurfaceId& child_id) {
+  void RemoveSurfaceReference(const SurfaceId& parent_id,
+                              const SurfaceId& child_id) {
     manager_->surface_manager()->RemoveSurfaceReferences(
-        {SurfaceReference(parent_id, child_id)});
+        {cc::SurfaceReference(parent_id, child_id)});
   }
 
-  void AddSurfaceReference(const viz::SurfaceId& parent_id,
-                           const viz::SurfaceId& child_id) {
+  void AddSurfaceReference(const SurfaceId& parent_id,
+                           const SurfaceId& child_id) {
     manager_->surface_manager()->AddSurfaceReferences(
-        {SurfaceReference(parent_id, child_id)});
+        {cc::SurfaceReference(parent_id, child_id)});
   }
 
   // Returns all the references where |surface_id| is the parent.
-  const base::flat_set<viz::SurfaceId>& GetReferencesFrom(
-      const viz::SurfaceId& surface_id) {
+  const base::flat_set<SurfaceId>& GetReferencesFrom(
+      const SurfaceId& surface_id) {
     return GetSurfaceManager().GetSurfacesReferencedByParent(surface_id);
   }
 
   // Returns all the references where |surface_id| is the child.
-  const base::flat_set<viz::SurfaceId>& GetReferencesFor(
-      const viz::SurfaceId& surface_id) {
+  const base::flat_set<SurfaceId>& GetReferencesFor(
+      const SurfaceId& surface_id) {
     return GetSurfaceManager().GetSurfacesThatReferenceChild(surface_id);
   }
 
-  // Temporary references are stored as a map in SurfaceManager. This method
+  // Temporary references are stored as a map in cc::SurfaceManager. This method
   // converts the map to a vector.
-  std::vector<viz::SurfaceId> GetAllTempReferences() {
-    std::vector<viz::SurfaceId> temp_references;
+  std::vector<SurfaceId> GetAllTempReferences() {
+    std::vector<SurfaceId> temp_references;
     for (auto& map_entry : GetSurfaceManager().temporary_references_)
       temp_references.push_back(map_entry.first);
     return temp_references;
@@ -111,9 +114,9 @@
  protected:
   // testing::Test:
   void SetUp() override {
-    // Start each test with a fresh SurfaceManager instance.
-    manager_ = base::MakeUnique<viz::FrameSinkManager>(
-        SurfaceManager::LifetimeType::REFERENCES);
+    // Start each test with a fresh cc::SurfaceManager instance.
+    manager_ = base::MakeUnique<FrameSinkManager>(
+        cc::SurfaceManager::LifetimeType::REFERENCES);
   }
   void TearDown() override {
     for (auto& support : supports_)
@@ -122,15 +125,15 @@
     manager_.reset();
   }
 
-  std::unordered_map<viz::FrameSinkId,
-                     std::unique_ptr<viz::CompositorFrameSinkSupport>,
-                     viz::FrameSinkIdHash>
+  std::unordered_map<FrameSinkId,
+                     std::unique_ptr<CompositorFrameSinkSupport>,
+                     FrameSinkIdHash>
       supports_;
-  std::unique_ptr<viz::FrameSinkManager> manager_;
+  std::unique_ptr<FrameSinkManager> manager_;
 };
 
-TEST_F(SurfaceManagerRefTest, AddReference) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceReferencesTest, AddReference) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   EXPECT_THAT(GetReferencesFor(id1),
@@ -138,9 +141,9 @@
   EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
 }
 
-TEST_F(SurfaceManagerRefTest, AddRemoveReference) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+TEST_F(SurfaceReferencesTest, AddRemoveReference) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
 
@@ -157,10 +160,10 @@
   EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
 }
 
-TEST_F(SurfaceManagerRefTest, NewSurfaceFromFrameSink) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
-  viz::SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+TEST_F(SurfaceReferencesTest, NewSurfaceFromFrameSink) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+  SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
@@ -168,7 +171,7 @@
 
   // |kFramesink2| received a CompositorFrame with a new size, so it destroys
   // |id2| and creates |id2_next|. No reference have been removed yet.
-  viz::SurfaceId id2_next = CreateSurface(kFrameSink2, 2);
+  SurfaceId id2_next = CreateSurface(kFrameSink2, 2);
   EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
   EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
 
@@ -190,10 +193,10 @@
   EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
-TEST_F(SurfaceManagerRefTest, ReferenceCycleGetsDeleted) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
-  viz::SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+TEST_F(SurfaceReferencesTest, ReferenceCycleGetsDeleted) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+  SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
@@ -215,9 +218,9 @@
   EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
-TEST_F(SurfaceManagerRefTest, SurfacesAreDeletedDuringGarbageCollection) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+TEST_F(SurfaceReferencesTest, SurfacesAreDeletedDuringGarbageCollection) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
@@ -241,10 +244,10 @@
   EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
 }
 
-TEST_F(SurfaceManagerRefTest, GarbageCollectionWorksRecusively) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
-  viz::SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+TEST_F(SurfaceReferencesTest, GarbageCollectionWorksRecusively) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+  SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
@@ -269,9 +272,9 @@
   EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
-TEST_F(SurfaceManagerRefTest, TryAddReferenceSameReferenceTwice) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+TEST_F(SurfaceReferencesTest, TryAddReferenceSameReferenceTwice) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
@@ -284,8 +287,8 @@
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
 }
 
-TEST_F(SurfaceManagerRefTest, AddingSelfReferenceFails) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+TEST_F(SurfaceReferencesTest, AddingSelfReferenceFails) {
+  SurfaceId id1 = CreateSurface(kFrameSink2, 1);
 
   // A temporary reference must exist to |id1|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
@@ -302,9 +305,9 @@
   EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
 }
 
-TEST_F(SurfaceManagerRefTest, RemovingNonexistantReferenceFails) {
-  viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+TEST_F(SurfaceReferencesTest, RemovingNonexistantReferenceFails) {
+  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
   // Removing non-existent reference should be ignored.
   AddSurfaceReference(id1, id2);
@@ -313,15 +316,15 @@
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
 }
 
-TEST_F(SurfaceManagerRefTest, AddSurfaceThenReference) {
+TEST_F(SurfaceReferencesTest, AddSurfaceThenReference) {
   // Create a new surface.
-  const viz::SurfaceId surface_id = CreateSurface(kFrameSink2, 1);
+  const SurfaceId surface_id = CreateSurface(kFrameSink2, 1);
 
   // A temporary reference must be added to |surface_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
 
   // Create |parent_id| and add a real reference from it to |surface_id|.
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(parent_id, surface_id);
 
   // The temporary reference to |surface_id| should be gone.
@@ -331,9 +334,9 @@
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id));
 }
 
-TEST_F(SurfaceManagerRefTest, AddSurfaceThenRootReference) {
+TEST_F(SurfaceReferencesTest, AddSurfaceThenRootReference) {
   // Create a new surface.
-  const viz::SurfaceId surface_id = CreateSurface(kFrameSink1, 1);
+  const SurfaceId surface_id = CreateSurface(kFrameSink1, 1);
 
   // Temporary reference should be added to |surface_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
@@ -348,17 +351,17 @@
               ElementsAre(surface_id));
 }
 
-TEST_F(SurfaceManagerRefTest, AddTwoSurfacesThenOneReference) {
+TEST_F(SurfaceReferencesTest, AddTwoSurfacesThenOneReference) {
   // Create two surfaces with different FrameSinkIds.
-  const viz::SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
-  const viz::SurfaceId surface_id2 = CreateSurface(kFrameSink3, 1);
+  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId surface_id2 = CreateSurface(kFrameSink3, 1);
 
   // Temporary reference should be added for both surfaces.
   EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(surface_id1, surface_id2));
 
   // Create |parent_id| and add a real reference from it to |surface_id1|.
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(parent_id, surface_id1);
 
   // Real reference must be added to |surface_id1| and the temporary reference
@@ -370,12 +373,12 @@
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
 }
 
-TEST_F(SurfaceManagerRefTest, AddSurfacesSkipReference) {
-  // Add two surfaces that have the same viz::FrameSinkId. This would happen
+TEST_F(SurfaceReferencesTest, AddSurfacesSkipReference) {
+  // Add two surfaces that have the same FrameSinkId. This would happen
   // when a client submits two CompositorFrames before parent submits a new
   // CompositorFrame.
-  const viz::SurfaceId surface_id1 = CreateSurface(kFrameSink2, 2);
-  const viz::SurfaceId surface_id2 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 2);
+  const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 1);
 
   // Temporary references should be added for both surfaces and they should be
   // stored in the order of creation.
@@ -384,7 +387,7 @@
 
   // Create |parent_id| and add a reference from it to |surface_id2| which was
   // created later.
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(parent_id, surface_id2);
 
   // The real reference should be added for |surface_id2| and the temporary
@@ -394,11 +397,11 @@
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id2));
 }
 
-TEST_F(SurfaceManagerRefTest, RemoveFirstTempReferenceOnly) {
-  // Add two surfaces that have the same viz::FrameSinkId. This would happen
+TEST_F(SurfaceReferencesTest, RemoveFirstTempReferenceOnly) {
+  // Add two surfaces that have the same FrameSinkId. This would happen
   // when a client submits two CFs before parent submits a new CF.
-  const viz::SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
-  const viz::SurfaceId surface_id2 = CreateSurface(kFrameSink2, 2);
+  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 2);
 
   // Temporary references should be added for both surfaces and they should be
   // stored in the order of creation.
@@ -407,7 +410,7 @@
 
   // Create |parent_id| and add a reference from it to |surface_id1| which was
   // created earlier.
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(parent_id, surface_id1);
 
   // The real reference should be added for |surface_id1| and its temporary
@@ -418,13 +421,13 @@
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
 }
 
-TEST_F(SurfaceManagerRefTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
-  const viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceReferencesTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
+  const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   // We create |id2| and never add a real reference to it. This leaves the
   // temporary reference.
-  const viz::SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id2));
   EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 
@@ -443,9 +446,9 @@
 
 // Checks that when a temporary reference is assigned an owner, if the owner is
 // invalidated then the temporary reference is also removed.
-TEST_F(SurfaceManagerRefTest, InvalidateTempReferenceOwnerRemovesReference) {
+TEST_F(SurfaceReferencesTest, InvalidateTempReferenceOwnerRemovesReference) {
   // Surface |id1| should have a temporary reference on creation.
-  const viz::SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
   // |id1| should have a temporary reference after an owner is assigned.
@@ -459,11 +462,11 @@
 
 // Checks that adding a surface reference clears the temporary reference and
 // ownership. Invalidating the old owner shouldn't do anything.
-TEST_F(SurfaceManagerRefTest, InvalidateHasNoEffectOnSurfaceReferences) {
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceReferencesTest, InvalidateHasNoEffectOnSurfaceReferences) {
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
 
-  const viz::SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+  const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
   GetSurfaceManager().AssignTemporaryReference(id1, kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
@@ -478,8 +481,8 @@
   ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
 }
 
-TEST_F(SurfaceManagerRefTest, CheckDropTemporaryReferenceWorks) {
-  const viz::SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceReferencesTest, CheckDropTemporaryReferenceWorks) {
+  const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
   // An example of why this could happen is the window server doesn't know the
@@ -492,12 +495,12 @@
 // Checks that we handle ownership and temporary references correctly when there
 // are multiple temporary references. This tests something like the parent
 // client crashing, so it's
-TEST_F(SurfaceManagerRefTest, TempReferencesWithClientCrash) {
-  const viz::SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceReferencesTest, TempReferencesWithClientCrash) {
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
   AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
 
-  const viz::SurfaceId id1a = CreateSurface(kFrameSink2, 1);
-  const viz::SurfaceId id1b = CreateSurface(kFrameSink2, 2);
+  const SurfaceId id1a = CreateSurface(kFrameSink2, 1);
+  const SurfaceId id1b = CreateSurface(kFrameSink2, 2);
 
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
 
@@ -507,7 +510,7 @@
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
 
   // If the parent client crashes then the FrameSink connection will be closed
-  // and the viz::FrameSinkId invalidated. The temporary reference |kFrameSink1|
+  // and the FrameSinkId invalidated. The temporary reference |kFrameSink1|
   // owns to |id2a| will be removed.
   DestroyCompositorFrameSinkSupport(kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1b));
@@ -519,4 +522,5 @@
   ASSERT_THAT(GetAllTempReferences(), IsEmpty());
 }
 
-}  // namespace cc
+}  // namespace test
+}  // namespace viz
diff --git a/components/viz/test/test_layer_tree_frame_sink.cc b/components/viz/test/test_layer_tree_frame_sink.cc
index c1708a1..a6f60dbe 100644
--- a/components/viz/test/test_layer_tree_frame_sink.cc
+++ b/components/viz/test/test_layer_tree_frame_sink.cc
@@ -22,8 +22,8 @@
 static constexpr FrameSinkId kLayerTreeFrameSinkId(1, 1);
 
 TestLayerTreeFrameSink::TestLayerTreeFrameSink(
-    scoped_refptr<cc::ContextProvider> compositor_context_provider,
-    scoped_refptr<cc::ContextProvider> worker_context_provider,
+    scoped_refptr<ContextProvider> compositor_context_provider,
+    scoped_refptr<ContextProvider> worker_context_provider,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     const cc::RendererSettings& renderer_settings,
diff --git a/components/viz/test/test_layer_tree_frame_sink.h b/components/viz/test/test_layer_tree_frame_sink.h
index 5ee1e80..e78b3d22 100644
--- a/components/viz/test/test_layer_tree_frame_sink.h
+++ b/components/viz/test/test_layer_tree_frame_sink.h
@@ -35,7 +35,7 @@
   // This passes the ContextProvider being used by LayerTreeHostImpl which
   // can be used for the OutputSurface optionally.
   virtual std::unique_ptr<cc::OutputSurface> CreateDisplayOutputSurface(
-      scoped_refptr<cc::ContextProvider> compositor_context_provider) = 0;
+      scoped_refptr<ContextProvider> compositor_context_provider) = 0;
 
   virtual void DisplayReceivedLocalSurfaceId(
       const LocalSurfaceId& local_surface_id) = 0;
@@ -56,8 +56,8 @@
   // Pass true for |force_disable_reclaim_resources| to act like the Display
   // is out-of-process and can't return resources synchronously.
   TestLayerTreeFrameSink(
-      scoped_refptr<cc::ContextProvider> compositor_context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<ContextProvider> compositor_context_provider,
+      scoped_refptr<ContextProvider> worker_context_provider,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       const cc::RendererSettings& renderer_settings,
diff --git a/content/browser/blob_storage/blob_dispatcher_host.cc b/content/browser/blob_storage/blob_dispatcher_host.cc
index bea813d..dd7209c 100644
--- a/content/browser/blob_storage/blob_dispatcher_host.cc
+++ b/content/browser/blob_storage/blob_dispatcher_host.cc
@@ -7,12 +7,14 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/fileapi/browser_file_system_helper.h"
 #include "content/common/fileapi/webblob_messages.h"
+#include "content/public/common/content_features.h"
 #include "ipc/ipc_platform_file.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_entry.h"
@@ -355,7 +357,12 @@
 }
 
 bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) {
-  return base::ContainsKey(blobs_inuse_map_, uuid);
+  // IsInUseInHost is not a security check, as renderers can arbitrarily start
+  // using blobs by sending an IncrementRefCount IPC. Furthermore with mojo
+  // blobs it doesn't make sense anymore to try to decide if a blob is in use in
+  // a process, so just always return true in that case.
+  return base::FeatureList::IsEnabled(features::kMojoBlobs) ||
+         base::ContainsKey(blobs_inuse_map_, uuid);
 }
 
 bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) {
diff --git a/content/browser/blob_storage/blob_storage_browsertest.cc b/content/browser/blob_storage/blob_storage_browsertest.cc
index ac516a8..9296e86 100644
--- a/content/browser/blob_storage/blob_storage_browsertest.cc
+++ b/content/browser/blob_storage/blob_storage_browsertest.cc
@@ -3,11 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -103,4 +105,36 @@
   RunAllBlockingPoolTasksUntilIdle();
 }
 
+class MojoBlobStorageBrowserTest : public BlobStorageBrowserTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(features::kMojoBlobs);
+    BlobStorageBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(MojoBlobStorageBrowserTest, BlobCombinations) {
+  SetBlobLimits();
+  SimpleTest(GetTestUrl("blob_storage", "blob_creation_and_slicing.html"));
+  storage::BlobMemoryController* memory_controller = GetMemoryController();
+  ASSERT_TRUE(memory_controller);
+  // Our exact usages depend on IPC message ordering & garbage collection.
+  // Since this is basically random, we just check bounds.
+  EXPECT_LT(0u, memory_controller->memory_usage());
+  EXPECT_LT(0ul, memory_controller->disk_usage());
+  EXPECT_GT(memory_controller->disk_usage(),
+            static_cast<uint64_t>(memory_controller->memory_usage()));
+  EXPECT_GT(limits_.max_blob_in_memory_space,
+            memory_controller->memory_usage());
+  EXPECT_GT(limits_.effective_max_disk_space, memory_controller->disk_usage());
+  shell()->Close();
+
+  // Make sure we run all file / io tasks.
+  base::RunLoop().RunUntilIdle();
+  RunAllBlockingPoolTasksUntilIdle();
+}
+
 }  // namespace content
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 5d8124c..0c48b7f0 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -875,6 +875,16 @@
     RenderProcessHost::SetRunRendererInProcess(true);
 #endif
 
+  // Initialize origins that are whitelisted for process isolation.  Must be
+  // done after base::FeatureList is initialized, but before any navigations
+  // can happen.
+  std::vector<url::Origin> origins =
+      GetContentClient()->browser()->GetOriginsRequiringDedicatedProcess();
+  ChildProcessSecurityPolicyImpl* policy =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+  for (auto origin : origins)
+    policy->AddIsolatedOrigin(origin);
+
   EVP_set_buggy_rsa_parser(
       base::FeatureList::IsEnabled(features::kBuggyRSAParser));
 
diff --git a/content/browser/compositor/browser_compositor_output_surface.cc b/content/browser/compositor/browser_compositor_output_surface.cc
index cdf87e5..888ceb4 100644
--- a/content/browser/compositor/browser_compositor_output_surface.cc
+++ b/content/browser/compositor/browser_compositor_output_surface.cc
@@ -20,7 +20,7 @@
 namespace content {
 
 BrowserCompositorOutputSurface::BrowserCompositorOutputSurface(
-    scoped_refptr<cc::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
     std::unique_ptr<viz::CompositorOverlayCandidateValidator>
         overlay_candidate_validator)
diff --git a/content/browser/compositor/browser_compositor_output_surface.h b/content/browser/compositor/browser_compositor_output_surface.h
index 84d56910d1..fabf348 100644
--- a/content/browser/compositor/browser_compositor_output_surface.h
+++ b/content/browser/compositor/browser_compositor_output_surface.h
@@ -50,7 +50,7 @@
  protected:
   // Constructor used by the accelerated implementation.
   BrowserCompositorOutputSurface(
-      scoped_refptr<cc::ContextProvider> context,
+      scoped_refptr<viz::ContextProvider> context,
       const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
       std::unique_ptr<viz::CompositorOverlayCandidateValidator>
           overlay_candidate_validator);
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 2e19825..7492ff0 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -104,7 +104,7 @@
 #include "content/browser/compositor/vulkan_browser_compositor_output_surface.h"
 #endif
 
-using cc::ContextProvider;
+using viz::ContextProvider;
 using gpu::gles2::GLES2Interface;
 
 namespace {
@@ -830,8 +830,7 @@
 
 viz::GLHelper* GpuProcessTransportFactory::GetGLHelper() {
   if (!gl_helper_ && !per_compositor_data_.empty()) {
-    scoped_refptr<cc::ContextProvider> provider =
-        SharedMainThreadContextProvider();
+    scoped_refptr<ContextProvider> provider = SharedMainThreadContextProvider();
     if (provider.get())
       gl_helper_.reset(
           new viz::GLHelper(provider->ContextGL(), provider->ContextSupport()));
@@ -859,7 +858,7 @@
 }
 #endif
 
-scoped_refptr<cc::ContextProvider>
+scoped_refptr<ContextProvider>
 GpuProcessTransportFactory::SharedMainThreadContextProvider() {
   if (shared_main_thread_contexts_)
     return shared_main_thread_contexts_;
@@ -934,7 +933,7 @@
   // new resources are created if needed.
   // Kill shared contexts for both threads in tandem so they are always in
   // the same share group.
-  scoped_refptr<cc::ContextProvider> lost_shared_main_thread_contexts =
+  scoped_refptr<ContextProvider> lost_shared_main_thread_contexts =
       shared_main_thread_contexts_;
   shared_main_thread_contexts_  = NULL;
 
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index f209923..52175d0 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -54,7 +54,8 @@
   // ui::ContextFactory implementation.
   void CreateLayerTreeFrameSink(
       base::WeakPtr<ui::Compositor> compositor) override;
-  scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider() override;
+  scoped_refptr<viz::ContextProvider> SharedMainThreadContextProvider()
+      override;
   double GetRefreshRate() const override;
   gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
   cc::TaskGraphRunner* GetTaskGraphRunner() override;
diff --git a/content/browser/compositor/image_transport_factory_browsertest.cc b/content/browser/compositor/image_transport_factory_browsertest.cc
index 4042b70..768d897 100644
--- a/content/browser/compositor/image_transport_factory_browsertest.cc
+++ b/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include "base/run_loop.h"
 #include "build/build_config.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "content/browser/compositor/owned_mailbox.h"
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/test/content_browser_test.h"
diff --git a/content/browser/compositor/reflector_impl_unittest.cc b/content/browser/compositor/reflector_impl_unittest.cc
index f1879dd8..cb14b1c 100644
--- a/content/browser/compositor/reflector_impl_unittest.cc
+++ b/content/browser/compositor/reflector_impl_unittest.cc
@@ -78,7 +78,7 @@
 
 class TestOutputSurface : public BrowserCompositorOutputSurface {
  public:
-  TestOutputSurface(scoped_refptr<cc::ContextProvider> context_provider)
+  TestOutputSurface(scoped_refptr<viz::ContextProvider> context_provider)
       : BrowserCompositorOutputSurface(std::move(context_provider),
                                        UpdateVSyncParametersCallback(),
                                        CreateTestValidatorOzone()) {}
diff --git a/content/browser/compositor/reflector_texture.cc b/content/browser/compositor/reflector_texture.cc
index 5301a3a..35dcfff 100644
--- a/content/browser/compositor/reflector_texture.cc
+++ b/content/browser/compositor/reflector_texture.cc
@@ -12,7 +12,7 @@
 
 namespace content {
 
-ReflectorTexture::ReflectorTexture(cc::ContextProvider* context_provider)
+ReflectorTexture::ReflectorTexture(viz::ContextProvider* context_provider)
     : texture_id_(0) {
   viz::GLHelper* shared_helper =
       ImageTransportFactory::GetInstance()->GetGLHelper();
diff --git a/content/browser/compositor/reflector_texture.h b/content/browser/compositor/reflector_texture.h
index a111b6fa..d5531a36 100644
--- a/content/browser/compositor/reflector_texture.h
+++ b/content/browser/compositor/reflector_texture.h
@@ -14,16 +14,13 @@
 #include "content/browser/compositor/owned_mailbox.h"
 #include "content/common/content_export.h"
 
-namespace cc {
-class ContextProvider;
-}
-
 namespace gfx {
 class Rect;
 class Size;
 }
 
 namespace viz {
+class ContextProvider;
 class GLHelper;
 }
 
@@ -32,7 +29,7 @@
 // Create and manages texture mailbox to be used by Reflector.
 class CONTENT_EXPORT ReflectorTexture {
  public:
-  explicit ReflectorTexture(cc::ContextProvider* provider);
+  explicit ReflectorTexture(viz::ContextProvider* provider);
   ~ReflectorTexture();
 
   void CopyTextureFullImage(const gfx::Size& size);
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.cc b/content/browser/compositor/test/no_transport_image_transport_factory.cc
index cc12f631..e579b158 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.cc
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.cc
@@ -9,9 +9,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "build/build_config.h"
-#include "cc/output/context_provider.h"
 #include "cc/surfaces/surface_manager.h"
 #include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "ui/compositor/compositor.h"
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.h b/content/browser/compositor/test/no_transport_image_transport_factory.h
index e0e3766..17a8688 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.h
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.h
@@ -15,14 +15,14 @@
 #include "content/browser/compositor/image_transport_factory.h"
 #include "ui/compositor/test/in_process_context_factory.h"
 
-namespace cc {
-class ContextProvider;
-}
-
 namespace ui {
 class InProcessContextFactory;
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace content {
 
 // An ImageTransportFactory that disables transport.
@@ -47,7 +47,7 @@
   viz::FrameSinkManagerImpl frame_sink_manager_;
   viz::HostFrameSinkManager host_frame_sink_manager_;
   ui::InProcessContextFactory context_factory_;
-  scoped_refptr<cc::ContextProvider> context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
   std::unique_ptr<viz::GLHelper> gl_helper_;
 
   DISALLOW_COPY_AND_ASSIGN(NoTransportImageTransportFactory);
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index 7e78825..8e8aac3b 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -66,7 +66,8 @@
       background_color_(SK_ColorWHITE),
       weak_factory_(this) {
   if (!service_manager::ServiceManagerIsRemote()) {
-    GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
+    GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(
+        frame_sink_id_);
     CreateCompositorFrameSinkSupport();
   }
 }
@@ -74,8 +75,10 @@
 RenderWidgetHostViewChildFrame::~RenderWidgetHostViewChildFrame() {
   if (!service_manager::ServiceManagerIsRemote()) {
     ResetCompositorFrameSinkSupport();
-    if (GetFrameSinkManager())
-      GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
+    if (GetFrameSinkManager()) {
+      GetFrameSinkManager()->surface_manager()->InvalidateFrameSinkId(
+          frame_sink_id_);
+    }
   }
 }
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index ae765ed..4f18ae6 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -32,7 +32,6 @@
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
 #include "cc/output/compositor_frame.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/output_surface_frame.h"
@@ -43,6 +42,7 @@
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/display/display.h"
@@ -465,7 +465,7 @@
       num_successive_context_creation_failures_(0),
       layer_tree_frame_sink_request_pending_(false),
       weak_factory_(this) {
-  GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(frame_sink_id_);
   DCHECK(client);
   DCHECK(root_window);
   DCHECK(root_window->GetLayer() == nullptr);
@@ -483,7 +483,8 @@
   root_window_->SetLayer(nullptr);
   // Clean-up any surface references.
   SetSurface(NULL);
-  GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->surface_manager()->InvalidateFrameSinkId(
+      frame_sink_id_);
 }
 
 bool CompositorImpl::IsForSubframe() {
@@ -783,7 +784,7 @@
           shared_context,
           ui::command_buffer_metrics::DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT);
   if (!context_provider->BindToCurrentThread()) {
-    LOG(ERROR) << "Failed to init ContextProvider for compositor.";
+    LOG(ERROR) << "Failed to init viz::ContextProvider for compositor.";
     LOG_IF(FATAL, ++num_successive_context_creation_failures_ >= 2)
         << "Too many context creation failures. Giving up... ";
     HandlePendingLayerTreeFrameSinkRequest();
@@ -801,7 +802,7 @@
 void CompositorImpl::InitializeDisplay(
     std::unique_ptr<cc::OutputSurface> display_output_surface,
     scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
-    scoped_refptr<cc::ContextProvider> context_provider) {
+    scoped_refptr<viz::ContextProvider> context_provider) {
   DCHECK(layer_tree_frame_sink_request_pending_);
 
   pending_frames_ = 0;
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 98c6c16..b55dc2a 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -134,7 +134,7 @@
   void InitializeDisplay(
       std::unique_ptr<cc::OutputSurface> display_output_surface,
       scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
-      scoped_refptr<cc::ContextProvider> context_provider);
+      scoped_refptr<viz::ContextProvider> context_provider);
   void DidSwapBuffers();
 
   bool HavePendingReadbacks();
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index a27268b..44a3a36 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -56,6 +56,7 @@
   factory->GetContextFactory()->AddObserver(this);
   factory->GetContextFactoryPrivate()
       ->GetFrameSinkManager()
+      ->surface_manager()
       ->RegisterFrameSinkId(frame_sink_id_);
   CreateCompositorFrameSinkSupport();
 }
@@ -768,6 +769,7 @@
 
   factory->GetContextFactoryPrivate()
       ->GetFrameSinkManager()
+      ->surface_manager()
       ->InvalidateFrameSinkId(frame_sink_id_);
 
   DCHECK(!vsync_manager_.get());
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 069e484..31e7f8e 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -45,6 +45,7 @@
           "shape_detection::mojom::FaceDetectionProvider",
           "resource_coordinator::mojom::CoordinationUnit",
           "shape_detection::mojom::TextDetection",
+          "storage::mojom::BlobRegistry",
           "ui::mojom::Gpu"
         ],
         "geolocation_config": [
diff --git a/content/public/browser/android/compositor.h b/content/public/browser/android/compositor.h
index 42213059..618bbf5 100644
--- a/content/public/browser/android/compositor.h
+++ b/content/public/browser/android/compositor.h
@@ -15,7 +15,6 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace cc {
-class ContextProvider;
 class Layer;
 }
 
@@ -32,6 +31,10 @@
 class UIResourceProvider;
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace content {
 class CompositorClient;
 
@@ -47,7 +50,7 @@
   // Creates a GL context for the provided |handle|. If a null handle is passed,
   // an offscreen context is created. This must be called on the UI thread.
   using ContextProviderCallback =
-      base::Callback<void(scoped_refptr<cc::ContextProvider>)>;
+      base::Callback<void(scoped_refptr<viz::ContextProvider>)>;
   static void CreateContextProvider(
       gpu::SurfaceHandle handle,
       gpu::gles2::ContextCreationAttribHelper attributes,
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index e32549dd..5193ae1 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -23,6 +23,7 @@
 #include "storage/browser/quota/quota_manager.h"
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 
@@ -139,6 +140,11 @@
   return true;
 }
 
+std::vector<url::Origin>
+ContentBrowserClient::GetOriginsRequiringDedicatedProcess() {
+  return std::vector<url::Origin>();
+}
+
 std::string ContentBrowserClient::GetApplicationLocale() {
   return "en-US";
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 41cd656e..7cd8c69b 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -310,6 +310,10 @@
   // current SiteInstance, if it does not yet have a site.
   virtual bool ShouldAssignSiteForURL(const GURL& url);
 
+  // Allows the embedder to provide a list of origins that require a dedicated
+  // process.
+  virtual std::vector<url::Origin> GetOriginsRequiringDedicatedProcess();
+
   // Allows the embedder to pass extra command line flags.
   // switches::kProcessType will already be set at this point.
   virtual void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4c4ce6e..88b0b0f 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -265,6 +265,11 @@
 const base::Feature kSharedArrayBuffer{"SharedArrayBuffer",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
+// An experiment to require process isolation for the sign-in origin,
+// https://accounts.google.com.  Launch bug: https://crbug.com/739418.
+const base::Feature kSignInProcessIsolation{"sign-in-process-isolation",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 // An experiment for skipping compositing small scrollers.
 const base::Feature kSkipCompositingSmallScrollers{
     "SkipCompositingSmallScrollers", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 1c49647..ea57fb06 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -71,6 +71,7 @@
 CONTENT_EXPORT extern const base::Feature kServiceWorkerNavigationPreload;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerScriptStreaming;
 CONTENT_EXPORT extern const base::Feature kSharedArrayBuffer;
+CONTENT_EXPORT extern const base::Feature kSignInProcessIsolation;
 CONTENT_EXPORT extern const base::Feature kSkipCompositingSmallScrollers;
 CONTENT_EXPORT extern const base::Feature kSlimmingPaintInvalidation;
 CONTENT_EXPORT extern const base::Feature kTimerThrottlingForHiddenFrames;
diff --git a/content/renderer/DEPS b/content/renderer/DEPS
index a77566d..ba452da 100644
--- a/content/renderer/DEPS
+++ b/content/renderer/DEPS
@@ -10,7 +10,6 @@
   "+components/variations/child_process_field_trial_syncer.h",
   "+components/viz/client",
   "+components/viz/common",
-
   "+cc/blink",
   "+content/public/child",
   "+content/public/renderer",
diff --git a/content/renderer/android/DEPS b/content/renderer/android/DEPS
index 3f8f3da..c6c21da5 100644
--- a/content/renderer/android/DEPS
+++ b/content/renderer/android/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/viz/common/gpu",
   "+components/viz/service/display",
   "+components/viz/service/frame_sinks",
   "+third_party/libphonenumber",  # For phone number detection.
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.cc b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
index 63a7623..3707818 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.cc
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
@@ -14,7 +14,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "cc/output/compositor_frame.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/layer_tree_frame_sink_client.h"
 #include "cc/output/output_surface.h"
 #include "cc/output/output_surface_frame.h"
@@ -23,6 +22,7 @@
 #include "cc/output/texture_mailbox_deleter.h"
 #include "cc/quads/render_pass.h"
 #include "cc/quads/surface_draw_quad.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/local_surface_id_allocator.h"
 #include "components/viz/service/display/display.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -106,8 +106,8 @@
 };
 
 SynchronousLayerTreeFrameSink::SynchronousLayerTreeFrameSink(
-    scoped_refptr<cc::ContextProvider> context_provider,
-    scoped_refptr<cc::ContextProvider> worker_context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> worker_context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     viz::SharedBitmapManager* shared_bitmap_manager,
     int routing_id,
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.h b/content/renderer/android/synchronous_layer_tree_frame_sink.h
index 3718d31..8a869c1 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.h
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.h
@@ -27,7 +27,6 @@
 
 namespace cc {
 class BeginFrameSource;
-class ContextProvider;
 }  // namespace cc
 
 namespace IPC {
@@ -37,6 +36,7 @@
 
 namespace viz {
 class CompositorFrameSinkSupport;
+class ContextProvider;
 class Display;
 class FrameSinkManager;
 class LocalSurfaceIdAllocator;
@@ -71,8 +71,8 @@
       public viz::CompositorFrameSinkSupportClient {
  public:
   SynchronousLayerTreeFrameSink(
-      scoped_refptr<cc::ContextProvider> context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       viz::SharedBitmapManager* shared_bitmap_manager,
       int routing_id,
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc
index 3192c99e..fde1f53 100644
--- a/content/renderer/child_frame_compositing_helper.cc
+++ b/content/renderer/child_frame_compositing_helper.cc
@@ -10,11 +10,11 @@
 #include "cc/layers/picture_image_layer.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/surface_layer.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/output/copy_output_result.h"
 #include "cc/paint/paint_image.h"
 #include "cc/resources/single_release_callback.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/sequence_surface_reference_factory.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/common/browser_plugin/browser_plugin_messages.h"
diff --git a/content/renderer/layout_test_dependencies.h b/content/renderer/layout_test_dependencies.h
index df8feef..2367e4a6 100644
--- a/content/renderer/layout_test_dependencies.h
+++ b/content/renderer/layout_test_dependencies.h
@@ -11,7 +11,6 @@
 #include "base/memory/ref_counted.h"
 
 namespace cc {
-class ContextProvider;
 class CopyOutputRequest;
 class LayerTreeFrameSink;
 class SwapPromise;
@@ -22,6 +21,10 @@
 class GpuMemoryBufferManager;
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace content {
 class CompositorDependencies;
 
@@ -32,8 +35,8 @@
   virtual std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(
       int32_t routing_id,
       scoped_refptr<gpu::GpuChannelHost> gpu_channel,
-      scoped_refptr<cc::ContextProvider> compositor_context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       CompositorDependencies* deps) = 0;
 
diff --git a/content/renderer/media/android/stream_texture_factory.cc b/content/renderer/media/android/stream_texture_factory.cc
index fd8521e0..395455cd 100644
--- a/content/renderer/media/android/stream_texture_factory.cc
+++ b/content/renderer/media/android/stream_texture_factory.cc
@@ -6,7 +6,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "cc/output/context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index 3a48e80..a3972d1 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -11,7 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/unguessable_token.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/buffer_to_texture_target_map.h"
 #include "content/child/child_thread_impl.h"
 #include "content/public/common/service_names.mojom.h"
@@ -98,7 +98,7 @@
   if (context_provider_) {
     bool release_context_provider = false;
     {
-      cc::ContextProvider::ScopedContextLock lock(context_provider_);
+      viz::ContextProvider::ScopedContextLock lock(context_provider_);
       if (lock.ContextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
         context_provider_ = nullptr;
         release_context_provider = true;
@@ -181,7 +181,7 @@
 
   if (CheckContextLost())
     return false;
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   gpu::gles2::GLES2Interface* gles2 = lock.ContextGL();
   texture_ids->resize(count);
   texture_mailboxes->resize(count);
@@ -217,14 +217,14 @@
   if (CheckContextLost())
     return;
 
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   gpu::gles2::GLES2Interface* gles2 = lock.ContextGL();
   gles2->DeleteTextures(1, &texture_id);
   DCHECK_EQ(gles2->GetError(), static_cast<GLenum>(GL_NO_ERROR));
 }
 
 gpu::SyncToken GpuVideoAcceleratorFactoriesImpl::CreateSyncToken() {
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   gpu::gles2::GLES2Interface* gl = lock.ContextGL();
   gpu::SyncToken sync_token;
   const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
@@ -239,7 +239,7 @@
   if (CheckContextLost())
     return;
 
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   gpu::gles2::GLES2Interface* gles2 = lock.ContextGL();
   gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
 
@@ -253,7 +253,7 @@
   if (CheckContextLost())
     return;
 
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   gpu::gles2::GLES2Interface* gles2 = lock.ContextGL();
   gles2->ShallowFlushCHROMIUM();
 }
@@ -286,7 +286,7 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   if (CheckContextLost())
     return media::GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED;
-  cc::ContextProvider::ScopedContextLock lock(context_provider_);
+  viz::ContextProvider::ScopedContextLock lock(context_provider_);
   auto capabilities = context_provider_->ContextCapabilities();
   if (capabilities.image_ycbcr_420v)
     return media::GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB;
@@ -301,12 +301,12 @@
 class ScopedGLContextLockImpl
     : public media::GpuVideoAcceleratorFactories::ScopedGLContextLock {
  public:
-  ScopedGLContextLockImpl(cc::ContextProvider* context_provider)
+  ScopedGLContextLockImpl(viz::ContextProvider* context_provider)
       : lock_(context_provider) {}
   gpu::gles2::GLES2Interface* ContextGL() override { return lock_.ContextGL(); }
 
  private:
-  cc::ContextProvider::ScopedContextLock lock_;
+  viz::ContextProvider::ScopedContextLock lock_;
 };
 }  // namespace
 
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index ec83c24..f10b79e 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -50,7 +50,7 @@
 }
 
 void RendererWindowTreeClient::RequestLayerTreeFrameSink(
-    scoped_refptr<cc::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     const LayerTreeFrameSinkCallback& callback) {
   DCHECK(pending_layer_tree_frame_sink_callback_.is_null());
@@ -74,7 +74,7 @@
 }
 
 void RendererWindowTreeClient::RequestLayerTreeFrameSinkInternal(
-    scoped_refptr<cc::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     const LayerTreeFrameSinkCallback& callback) {
   cc::mojom::CompositorFrameSinkPtrInfo sink_info;
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h
index 63f61e2..89d618a 100644
--- a/content/renderer/mus/renderer_window_tree_client.h
+++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -12,13 +12,16 @@
 
 namespace cc {
 class LayerTreeFrameSink;
-class ContextProvider;
 }
 
 namespace gpu {
 class GpuMemoryBufferManager;
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace content {
 
 // ui.mojom.WindowTreeClient implementation for RenderWidget. This lives and
@@ -43,7 +46,7 @@
   using LayerTreeFrameSinkCallback =
       base::Callback<void(std::unique_ptr<cc::LayerTreeFrameSink>)>;
   void RequestLayerTreeFrameSink(
-      scoped_refptr<cc::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       const LayerTreeFrameSinkCallback& callback);
 
@@ -52,7 +55,7 @@
   ~RendererWindowTreeClient() override;
 
   void RequestLayerTreeFrameSinkInternal(
-      scoped_refptr<cc::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       const LayerTreeFrameSinkCallback& callback);
 
@@ -160,7 +163,7 @@
 
   const int routing_id_;
   ui::Id root_window_id_;
-  scoped_refptr<cc::ContextProvider> pending_context_provider_;
+  scoped_refptr<viz::ContextProvider> pending_context_provider_;
   gpu::GpuMemoryBufferManager* pending_gpu_memory_buffer_manager_ = nullptr;
   LayerTreeFrameSinkCallback pending_layer_tree_frame_sink_callback_;
   ui::mojom::WindowTreePtr tree_;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index e22d4f20..e0c03b8 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1413,7 +1413,7 @@
     scoped_refptr<ui::ContextProviderCommandBuffer> shared_context_provider =
         gpu_factories_.back()->ContextProviderMainThread();
     if (shared_context_provider) {
-      cc::ContextProvider::ScopedContextLock lock(
+      viz::ContextProvider::ScopedContextLock lock(
           shared_context_provider.get());
       if (lock.ContextGL()->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
         return gpu_factories_.back().get();
@@ -2390,7 +2390,7 @@
   // Try to reuse existing shared worker context provider.
   if (shared_worker_context_provider_) {
     // Note: If context is lost, delete reference after releasing the lock.
-    cc::ContextProvider::ScopedContextLock lock(
+    viz::ContextProvider::ScopedContextLock lock(
         shared_worker_context_provider_.get());
     if (shared_worker_context_provider_->ContextGL()
             ->GetGraphicsResetStatusKHR() == GL_NO_ERROR)
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc
index cf5b1b2..dc5d5dd4 100644
--- a/content/test/layouttest_support.cc
+++ b/content/test/layouttest_support.cc
@@ -334,8 +334,8 @@
   std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(
       int32_t routing_id,
       scoped_refptr<gpu::GpuChannelHost> gpu_channel,
-      scoped_refptr<cc::ContextProvider> compositor_context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
+      scoped_refptr<viz::ContextProvider> compositor_context_provider,
+      scoped_refptr<viz::ContextProvider> worker_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       CompositorDependencies* deps) override {
     // This could override the GpuChannel for a LayerTreeFrameSink that was
@@ -386,7 +386,8 @@
 
   // TestLayerTreeFrameSinkClient implementation.
   std::unique_ptr<cc::OutputSurface> CreateDisplayOutputSurface(
-      scoped_refptr<cc::ContextProvider> compositor_context_provider) override {
+      scoped_refptr<viz::ContextProvider> compositor_context_provider)
+      override {
     // This is for an offscreen context for the compositor. So the default
     // framebuffer doesn't need alpha, depth, stencil, antialiasing.
     gpu::gles2::ContextCreationAttribHelper attributes;
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index 0388136..5f16fa9 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -69,12 +69,13 @@
       background_color_(SK_ColorWHITE) {
 #if defined(OS_ANDROID)
   frame_sink_id_ = AllocateFrameSinkId();
-  GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(frame_sink_id_);
 #else
   // Not all tests initialize or need an image transport factory.
   if (ImageTransportFactory::GetInstance()) {
     frame_sink_id_ = AllocateFrameSinkId();
-    GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
+    GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(
+        frame_sink_id_);
   }
 #endif
 
@@ -91,7 +92,7 @@
 TestRenderWidgetHostView::~TestRenderWidgetHostView() {
   viz::FrameSinkManager* manager = GetFrameSinkManager();
   if (manager) {
-    manager->InvalidateFrameSinkId(frame_sink_id_);
+    manager->surface_manager()->InvalidateFrameSinkId(frame_sink_id_);
   }
 }
 
diff --git a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.h b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.h
index b7d593b..88768e4 100644
--- a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.h
+++ b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.h
@@ -131,6 +131,8 @@
       ItemFactoryCallback* factory_callback,
       ItemStoreDeleter* deleter_callback);
 
+  const std::string& crypto_key_for_testing() const { return crypto_key_; }
+
  private:
   // Maps a data item ID to the data item instance.
   using DataItemMap =
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 9e954a4..0632fc0a 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -244,25 +244,23 @@
       content_watcher_(new ContentWatcher()),
       source_map_(&ResourceBundle::GetSharedInstance()),
       v8_schema_registry_(new V8SchemaRegistry),
-      ipc_message_sender_(IPCMessageSender::CreateMainThreadIPCMessageSender()),
       user_script_set_manager_observer_(this),
       activity_logging_enabled_(false) {
   const base::CommandLine& command_line =
       *(base::CommandLine::ForCurrentProcess());
 
+  std::unique_ptr<IPCMessageSender> ipc_message_sender =
+      IPCMessageSender::CreateMainThreadIPCMessageSender();
   if (FeatureSwitch::native_crx_bindings()->IsEnabled()) {
     // This Unretained is safe because the IPCMessageSender is guaranteed to
     // outlive the bindings system.
     auto system = base::MakeUnique<NativeExtensionBindingsSystem>(
-        base::Bind(&IPCMessageSender::SendRequestIPC,
-                   base::Unretained(ipc_message_sender_.get())),
-        base::Bind(&SendEventListenersIPC));
+        std::move(ipc_message_sender), base::Bind(&SendEventListenersIPC));
     delegate_->InitializeBindingsSystem(this, system->api_system());
     bindings_system_ = std::move(system);
   } else {
     bindings_system_ = base::MakeUnique<JsExtensionBindingsSystem>(
-        &source_map_,
-        base::MakeUnique<RequestSender>(ipc_message_sender_.get()));
+        &source_map_, std::move(ipc_message_sender));
   }
 
   set_idle_notifications_ =
@@ -648,7 +646,6 @@
                                      const base::ListValue& response,
                                      const std::string& error) {
   bindings_system_->HandleResponse(request_id, success, response, error);
-  ipc_message_sender_->SendOnRequestResponseReceivedIPC(request_id);
 }
 
 void Dispatcher::DispatchEvent(const std::string& extension_id,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 8b3763e6..25800fb 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -56,7 +56,6 @@
 class ContentWatcher;
 class DispatcherDelegate;
 class ExtensionBindingsSystem;
-class IPCMessageSender;
 class ScriptContext;
 class ScriptInjectionManager;
 struct EventFilteringInfo;
@@ -280,9 +279,6 @@
   // Cache for the v8 representation of extension API schemas.
   std::unique_ptr<V8SchemaRegistry> v8_schema_registry_;
 
-  // A helper to dispatch event and request IPCs.
-  std::unique_ptr<IPCMessageSender> ipc_message_sender_;
-
   // The bindings system associated with the main thread.
   std::unique_ptr<ExtensionBindingsSystem> bindings_system_;
 
diff --git a/extensions/renderer/extension_bindings_system.h b/extensions/renderer/extension_bindings_system.h
index dc16a81..ff32b4a 100644
--- a/extensions/renderer/extension_bindings_system.h
+++ b/extensions/renderer/extension_bindings_system.h
@@ -12,6 +12,7 @@
 }
 
 namespace extensions {
+class IPCMessageSender;
 class RequestSender;
 class ScriptContext;
 struct EventFilteringInfo;
@@ -54,6 +55,9 @@
                               const base::ListValue& response,
                               const std::string& error) = 0;
 
+  // Returns the associated IPC message sender.
+  virtual IPCMessageSender* GetIPCMessageSender() = 0;
+
   // Returns the associated RequestSender, if any.
   // TODO(devlin): Factor this out.
   virtual RequestSender* GetRequestSender() = 0;
diff --git a/extensions/renderer/js_extension_bindings_system.cc b/extensions/renderer/js_extension_bindings_system.cc
index de7e589..2e3b575 100644
--- a/extensions/renderer/js_extension_bindings_system.cc
+++ b/extensions/renderer/js_extension_bindings_system.cc
@@ -5,6 +5,7 @@
 #include "extensions/renderer/js_extension_bindings_system.h"
 
 #include "base/command_line.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_split.h"
 #include "content/public/child/v8_value_converter.h"
 #include "content/public/common/content_switches.h"
@@ -18,7 +19,9 @@
 #include "extensions/common/manifest_handlers/externally_connectable.h"
 #include "extensions/renderer/binding_generating_native_handler.h"
 #include "extensions/renderer/event_bindings.h"
+#include "extensions/renderer/ipc_message_sender.h"
 #include "extensions/renderer/renderer_extension_registry.h"
+#include "extensions/renderer/request_sender.h"
 #include "extensions/renderer/resource_bundle_source_map.h"
 #include "extensions/renderer/script_context.h"
 #include "gin/converter.h"
@@ -134,8 +137,11 @@
 
 JsExtensionBindingsSystem::JsExtensionBindingsSystem(
     ResourceBundleSourceMap* source_map,
-    std::unique_ptr<RequestSender> request_sender)
-    : source_map_(source_map), request_sender_(std::move(request_sender)) {}
+    std::unique_ptr<IPCMessageSender> ipc_message_sender)
+    : source_map_(source_map),
+      ipc_message_sender_(std::move(ipc_message_sender)),
+      request_sender_(
+          base::MakeUnique<RequestSender>(ipc_message_sender_.get())) {}
 
 JsExtensionBindingsSystem::~JsExtensionBindingsSystem() {}
 
@@ -224,12 +230,17 @@
                                                const base::ListValue& response,
                                                const std::string& error) {
   request_sender_->HandleResponse(request_id, success, response, error);
+  ipc_message_sender_->SendOnRequestResponseReceivedIPC(request_id);
 }
 
 RequestSender* JsExtensionBindingsSystem::GetRequestSender() {
   return request_sender_.get();
 }
 
+IPCMessageSender* JsExtensionBindingsSystem::GetIPCMessageSender() {
+  return ipc_message_sender_.get();
+}
+
 void JsExtensionBindingsSystem::DispatchEventInContext(
     const std::string& event_name,
     const base::ListValue* event_args,
diff --git a/extensions/renderer/js_extension_bindings_system.h b/extensions/renderer/js_extension_bindings_system.h
index a51f36d..d9754167 100644
--- a/extensions/renderer/js_extension_bindings_system.h
+++ b/extensions/renderer/js_extension_bindings_system.h
@@ -12,14 +12,14 @@
 #include "extensions/renderer/extension_bindings_system.h"
 
 namespace extensions {
-class RequestSender;
+class IPCMessageSender;
 class ResourceBundleSourceMap;
 
 // The bindings system using the traditional JS-injection style bindings.
 class JsExtensionBindingsSystem : public ExtensionBindingsSystem {
  public:
   JsExtensionBindingsSystem(ResourceBundleSourceMap* source_map,
-                            std::unique_ptr<RequestSender> request_sender);
+                            std::unique_ptr<IPCMessageSender> request_sender);
   ~JsExtensionBindingsSystem() override;
 
   // ExtensionBindingsSystem:
@@ -37,6 +37,7 @@
                       const base::ListValue& response,
                       const std::string& error) override;
   RequestSender* GetRequestSender() override;
+  IPCMessageSender* GetIPCMessageSender() override;
 
  private:
   void RegisterBinding(const std::string& api_name,
@@ -45,6 +46,8 @@
 
   ResourceBundleSourceMap* source_map_ = nullptr;
 
+  std::unique_ptr<IPCMessageSender> ipc_message_sender_;
+
   std::unique_ptr<RequestSender> request_sender_;
 
   DISALLOW_COPY_AND_ASSIGN(JsExtensionBindingsSystem);
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index 750c20ef..b0b4727 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -21,6 +21,7 @@
 #include "extensions/renderer/console.h"
 #include "extensions/renderer/content_setting.h"
 #include "extensions/renderer/declarative_content_hooks_delegate.h"
+#include "extensions/renderer/ipc_message_sender.h"
 #include "extensions/renderer/module_system.h"
 #include "extensions/renderer/script_context.h"
 #include "extensions/renderer/script_context_set.h"
@@ -349,9 +350,9 @@
 }  // namespace
 
 NativeExtensionBindingsSystem::NativeExtensionBindingsSystem(
-    const SendRequestIPCMethod& send_request_ipc,
+    std::unique_ptr<IPCMessageSender> ipc_message_sender,
     const SendEventListenerIPCMethod& send_event_listener_ipc)
-    : send_request_ipc_(send_request_ipc),
+    : ipc_message_sender_(std::move(ipc_message_sender)),
       send_event_listener_ipc_(send_event_listener_ipc),
       api_system_(
           base::Bind(&CallJsFunction),
@@ -559,12 +560,17 @@
   api_system_.CompleteRequest(
       request_id, response,
       !success && error.empty() ? "Unknown error." : error);
+  ipc_message_sender_->SendOnRequestResponseReceivedIPC(request_id);
 }
 
 RequestSender* NativeExtensionBindingsSystem::GetRequestSender() {
   return nullptr;
 }
 
+IPCMessageSender* NativeExtensionBindingsSystem::GetIPCMessageSender() {
+  return ipc_message_sender_.get();
+}
+
 void NativeExtensionBindingsSystem::BindingAccessor(
     v8::Local<v8::Name> name,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
@@ -733,7 +739,8 @@
   params->worker_thread_id = -1;
   params->service_worker_version_id = kInvalidServiceWorkerVersionId;
 
-  send_request_ipc_.Run(script_context, std::move(params), request->thread);
+  ipc_message_sender_->SendRequestIPC(script_context, std::move(params),
+                                      request->thread);
 }
 
 void NativeExtensionBindingsSystem::OnEventListenerChanged(
diff --git a/extensions/renderer/native_extension_bindings_system.h b/extensions/renderer/native_extension_bindings_system.h
index 9d0b64aa5..ff71317 100644
--- a/extensions/renderer/native_extension_bindings_system.h
+++ b/extensions/renderer/native_extension_bindings_system.h
@@ -16,9 +16,8 @@
 #include "extensions/renderer/extension_bindings_system.h"
 #include "v8/include/v8.h"
 
-struct ExtensionHostMsg_Request_Params;
-
 namespace extensions {
+class IPCMessageSender;
 class ScriptContext;
 
 // The implementation of the Bindings System for extensions code with native
@@ -29,11 +28,6 @@
 // Designed to be used in a single thread, but for all contexts on that thread.
 class NativeExtensionBindingsSystem : public ExtensionBindingsSystem {
  public:
-  // TODO(devlin): Instead, pass in an IPCMessageSender.
-  using SendRequestIPCMethod =
-      base::Callback<void(ScriptContext*,
-                          std::unique_ptr<ExtensionHostMsg_Request_Params>,
-                          binding::RequestThread)>;
   using SendEventListenerIPCMethod =
       base::Callback<void(binding::EventListenersChanged,
                           ScriptContext*,
@@ -42,7 +36,7 @@
                           bool was_manual)>;
 
   NativeExtensionBindingsSystem(
-      const SendRequestIPCMethod& send_request_ipc,
+      std::unique_ptr<IPCMessageSender> ipc_message_sender,
       const SendEventListenerIPCMethod& send_event_listener_ipc);
   ~NativeExtensionBindingsSystem() override;
 
@@ -61,6 +55,7 @@
                       const base::ListValue& response,
                       const std::string& error) override;
   RequestSender* GetRequestSender() override;
+  IPCMessageSender* GetIPCMessageSender() override;
 
   APIBindingsSystem* api_system() { return &api_system_; }
 
@@ -101,8 +96,7 @@
   void GetJSBindingUtil(v8::Local<v8::Context> context,
                         v8::Local<v8::Value>* binding_util_out);
 
-  // Handler to send request IPCs. Abstracted out for testing purposes.
-  SendRequestIPCMethod send_request_ipc_;
+  std::unique_ptr<IPCMessageSender> ipc_message_sender_;
 
   // Handler to notify the browser of event registrations. Abstracted out for
   // testing purposes.
diff --git a/extensions/renderer/native_extension_bindings_system_unittest.cc b/extensions/renderer/native_extension_bindings_system_unittest.cc
index ee707bf..3c45e0bb 100644
--- a/extensions/renderer/native_extension_bindings_system_unittest.cc
+++ b/extensions/renderer/native_extension_bindings_system_unittest.cc
@@ -18,6 +18,7 @@
 #include "extensions/renderer/bindings/api_binding_test.h"
 #include "extensions/renderer/bindings/api_binding_test_util.h"
 #include "extensions/renderer/bindings/api_invocation_errors.h"
+#include "extensions/renderer/ipc_message_sender.h"
 #include "extensions/renderer/module_system.h"
 #include "extensions/renderer/safe_builtins.h"
 #include "extensions/renderer/script_context.h"
@@ -88,6 +89,29 @@
   return !value->IsUndefined();
 };
 
+class TestIPCMessageSender : public IPCMessageSender {
+ public:
+  TestIPCMessageSender() {}
+  ~TestIPCMessageSender() override {}
+
+  // IPCMessageSender:
+  void SendRequestIPC(ScriptContext* context,
+                      std::unique_ptr<ExtensionHostMsg_Request_Params> params,
+                      binding::RequestThread thread) override {
+    last_params_ = std::move(params);
+  }
+  void SendOnRequestResponseReceivedIPC(int request_id) override {}
+
+  const ExtensionHostMsg_Request_Params* last_params() const {
+    return last_params_.get();
+  }
+
+ private:
+  std::unique_ptr<ExtensionHostMsg_Request_Params> last_params_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestIPCMessageSender);
+};
+
 }  // namespace
 
 class NativeExtensionBindingsSystemUnittest : public APIBindingTest {
@@ -105,9 +129,10 @@
   void SetUp() override {
     render_thread_ = base::MakeUnique<content::MockRenderThread>();
     script_context_set_ = base::MakeUnique<ScriptContextSet>(&extension_ids_);
+    auto ipc_message_sender = base::MakeUnique<TestIPCMessageSender>();
+    ipc_message_sender_ = ipc_message_sender.get();
     bindings_system_ = base::MakeUnique<NativeExtensionBindingsSystem>(
-        base::Bind(&NativeExtensionBindingsSystemUnittest::MockSendRequestIPC,
-                   base::Unretained(this)),
+        std::move(ipc_message_sender),
         base::Bind(&NativeExtensionBindingsSystemUnittest::MockSendListenerIPC,
                    base::Unretained(this)));
     APIBindingTest::SetUp();
@@ -131,13 +156,6 @@
     APIBindingTest::TearDown();
   }
 
-  void MockSendRequestIPC(
-      ScriptContext* context,
-      std::unique_ptr<ExtensionHostMsg_Request_Params> params,
-      binding::RequestThread thread) {
-    last_params_ = std::move(params);
-  }
-
   void MockSendListenerIPC(binding::EventListenersChanged changed,
                            ScriptContext* context,
                            const std::string& event_name,
@@ -187,8 +205,10 @@
   NativeExtensionBindingsSystem* bindings_system() {
     return bindings_system_.get();
   }
-  bool has_last_params() const { return !!last_params_; }
-  const ExtensionHostMsg_Request_Params& last_params() { return *last_params_; }
+  bool has_last_params() const { return !!ipc_message_sender_->last_params(); }
+  const ExtensionHostMsg_Request_Params& last_params() {
+    return *ipc_message_sender_->last_params();
+  }
   StringSourceMap* source_map() { return &source_map_; }
   MockEventChangeHandler* event_change_handler() {
     return event_change_handler_.get();
@@ -200,6 +220,8 @@
   std::unique_ptr<ScriptContextSet> script_context_set_;
   std::vector<ScriptContext*> raw_script_contexts_;
   std::unique_ptr<NativeExtensionBindingsSystem> bindings_system_;
+  // The TestIPCMessageSender; owned by the bindings system.
+  TestIPCMessageSender* ipc_message_sender_ = nullptr;
 
   std::unique_ptr<ExtensionHostMsg_Request_Params> last_params_;
   std::unique_ptr<MockEventChangeHandler> event_change_handler_;
diff --git a/extensions/renderer/service_worker_data.cc b/extensions/renderer/service_worker_data.cc
index b20986a..357c2888 100644
--- a/extensions/renderer/service_worker_data.cc
+++ b/extensions/renderer/service_worker_data.cc
@@ -5,19 +5,16 @@
 #include "extensions/renderer/service_worker_data.h"
 
 #include "extensions/renderer/extension_bindings_system.h"
-#include "extensions/renderer/ipc_message_sender.h"
 
 namespace extensions {
 
 ServiceWorkerData::ServiceWorkerData(
     int64_t service_worker_version_id,
     ScriptContext* context,
-    std::unique_ptr<ExtensionBindingsSystem> bindings_system,
-    std::unique_ptr<IPCMessageSender> ipc_message_sender)
+    std::unique_ptr<ExtensionBindingsSystem> bindings_system)
     : service_worker_version_id_(service_worker_version_id),
       context_(context),
       v8_schema_registry_(new V8SchemaRegistry),
-      ipc_message_sender_(std::move(ipc_message_sender)),
       bindings_system_(std::move(bindings_system)) {}
 
 ServiceWorkerData::~ServiceWorkerData() {}
diff --git a/extensions/renderer/service_worker_data.h b/extensions/renderer/service_worker_data.h
index 8f448b11..d9d780a 100644
--- a/extensions/renderer/service_worker_data.h
+++ b/extensions/renderer/service_worker_data.h
@@ -12,7 +12,6 @@
 
 namespace extensions {
 class ExtensionBindingsSystem;
-class IPCMessageSender;
 class ScriptContext;
 
 // Per ServiceWorker data in worker thread.
@@ -21,8 +20,7 @@
  public:
   ServiceWorkerData(int64_t service_worker_version_id,
                     ScriptContext* context,
-                    std::unique_ptr<ExtensionBindingsSystem> bindings_system,
-                    std::unique_ptr<IPCMessageSender> ipc_message_sender);
+                    std::unique_ptr<ExtensionBindingsSystem> bindings_system);
   ~ServiceWorkerData();
 
   V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); }
@@ -31,14 +29,12 @@
     return service_worker_version_id_;
   }
   ScriptContext* context() const { return context_; }
-  IPCMessageSender* ipc_message_sender() { return ipc_message_sender_.get(); }
 
  private:
   const int64_t service_worker_version_id_;
   ScriptContext* const context_;
 
   std::unique_ptr<V8SchemaRegistry> v8_schema_registry_;
-  std::unique_ptr<IPCMessageSender> ipc_message_sender_;
   std::unique_ptr<ExtensionBindingsSystem> bindings_system_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerData);
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index 8da4ad3..8455c10 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -135,7 +135,6 @@
   ServiceWorkerData* data = g_data_tls.Pointer()->Get();
   data->bindings_system()->HandleResponse(request_id, succeeded, response,
                                           error);
-  data->ipc_message_sender()->SendOnRequestResponseReceivedIPC(request_id);
 }
 
 void WorkerThreadDispatcher::OnDispatchEvent(
@@ -165,17 +164,13 @@
       // The Unretained below is safe since the IPC message sender outlives the
       // bindings system.
       bindings_system = base::MakeUnique<NativeExtensionBindingsSystem>(
-          base::Bind(&IPCMessageSender::SendRequestIPC,
-                     base::Unretained(ipc_message_sender.get())),
-          base::Bind(&SendEventListenersIPC));
+          std::move(ipc_message_sender), base::Bind(&SendEventListenersIPC));
     } else {
       bindings_system = base::MakeUnique<JsExtensionBindingsSystem>(
-          source_map,
-          base::MakeUnique<RequestSender>(ipc_message_sender.get()));
+          source_map, std::move(ipc_message_sender));
     }
     ServiceWorkerData* new_data = new ServiceWorkerData(
-        service_worker_version_id, context, std::move(bindings_system),
-        std::move(ipc_message_sender));
+        service_worker_version_id, context, std::move(bindings_system));
     g_data_tls.Pointer()->Set(new_data);
   }
 
diff --git a/gpu/ipc/BUILD.gn b/gpu/ipc/BUILD.gn
index 57571f8ae..7774238 100644
--- a/gpu/ipc/BUILD.gn
+++ b/gpu/ipc/BUILD.gn
@@ -23,6 +23,7 @@
 }
 target(link_target_type, "command_buffer_sources") {
   visibility = [ "//gpu/*" ]
+
   sources = [
     "gpu_in_process_thread_service.cc",
     "gpu_in_process_thread_service.h",
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 70d3381e..b491abf1 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -1,786 +1,744 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <memory>
-
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/process/process_metrics.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/perf_time_logger.h"
-#include "base/test/test_io_thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "ipc/ipc_channel_mojo.h"
-#include "ipc/ipc_test.mojom.h"
-#include "ipc/ipc_test_base.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-
-namespace IPC {
-namespace {
-
-// This class simply collects stats about abstract "events" (each of which has a
-// start time and an end time).
-class EventTimeTracker {
- public:
-  explicit EventTimeTracker(const char* name) : name_(name), count_(0) {}
-
-  void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
-    DCHECK(end >= start);
-    count_++;
-    base::TimeDelta duration = end - start;
-    total_duration_ += duration;
-    max_duration_ = std::max(max_duration_, duration);
-  }
-
-  void ShowResults() const {
-    VLOG(1) << name_ << " count: " << count_;
-    VLOG(1) << name_ << " total duration: " << total_duration_.InMillisecondsF()
-            << " ms";
-    VLOG(1) << name_ << " average duration: "
-            << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
-            << " ms";
-    VLOG(1) << name_ << " maximum duration: " << max_duration_.InMillisecondsF()
-            << " ms";
-  }
-
-  void Reset() {
-    count_ = 0;
-    total_duration_ = base::TimeDelta();
-    max_duration_ = base::TimeDelta();
-  }
-
- private:
-  const std::string name_;
-
-  uint64_t count_;
-  base::TimeDelta total_duration_;
-  base::TimeDelta max_duration_;
-
-  DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
-};
-
-class PerformanceChannelListener : public Listener {
- public:
-  explicit PerformanceChannelListener(const std::string& label)
-      : label_(label),
-        sender_(NULL),
-        msg_count_(0),
-        msg_size_(0),
-        count_down_(0),
-        latency_tracker_("Server messages") {
-    VLOG(1) << "Server listener up";
-  }
-
-  ~PerformanceChannelListener() override { VLOG(1) << "Server listener down"; }
-
-  void Init(Sender* sender) {
-    DCHECK(!sender_);
-    sender_ = sender;
-  }
-
-  // Call this before running the message loop.
-  void SetTestParams(int msg_count, size_t msg_size) {
-    DCHECK_EQ(0, count_down_);
-    msg_count_ = msg_count;
-    msg_size_ = msg_size;
-    count_down_ = msg_count_;
-    payload_ = std::string(msg_size_, 'a');
-  }
-
-  bool OnMessageReceived(const Message& message) override {
-    CHECK(sender_);
-
-    base::PickleIterator iter(message);
-    int64_t time_internal;
-    EXPECT_TRUE(iter.ReadInt64(&time_internal));
-    int msgid;
-    EXPECT_TRUE(iter.ReadInt(&msgid));
-    std::string reflected_payload;
-    EXPECT_TRUE(iter.ReadString(&reflected_payload));
-
-    // Include message deserialization in latency.
-    base::TimeTicks now = base::TimeTicks::Now();
-
-    if (reflected_payload == "hello") {
-      // Start timing on hello.
-      latency_tracker_.Reset();
-      DCHECK(!perf_logger_.get());
-      std::string test_name =
-          base::StringPrintf("IPC_%s_Perf_%dx_%u", label_.c_str(), msg_count_,
-                             static_cast<unsigned>(msg_size_));
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-    } else {
-      DCHECK_EQ(payload_.size(), reflected_payload.size());
-
-      latency_tracker_.AddEvent(
-          base::TimeTicks::FromInternalValue(time_internal), now);
-
-      CHECK(count_down_ > 0);
-      count_down_--;
-      if (count_down_ == 0) {
-        perf_logger_.reset();  // Stop the perf timer now.
-        latency_tracker_.ShowResults();
-        base::MessageLoop::current()->QuitWhenIdle();
-        return true;
-      }
-    }
-
-    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
-    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    msg->WriteInt(count_down_);
-    msg->WriteString(payload_);
-    sender_->Send(msg);
-    return true;
-  }
-
- private:
-  std::string label_;
-  Sender* sender_;
-  int msg_count_;
-  size_t msg_size_;
-
-  int count_down_;
-  std::string payload_;
-  EventTimeTracker latency_tracker_;
-  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
-};
-
-// This channel listener just replies to all messages with the exact same
-// message. It assumes each message has one string parameter. When the string
-// "quit" is sent, it will exit.
-class ChannelReflectorListener : public Listener {
- public:
-  ChannelReflectorListener()
-      : channel_(NULL), latency_tracker_("Client messages") {
-    VLOG(1) << "Client listener up";
-  }
-
-  ~ChannelReflectorListener() override {
-    VLOG(1) << "Client listener down";
-    latency_tracker_.ShowResults();
-  }
-
-  void Init(Channel* channel) {
-    DCHECK(!channel_);
-    channel_ = channel;
-  }
-
-  bool OnMessageReceived(const Message& message) override {
-    CHECK(channel_);
-
-    base::PickleIterator iter(message);
-    int64_t time_internal;
-    EXPECT_TRUE(iter.ReadInt64(&time_internal));
-    int msgid;
-    EXPECT_TRUE(iter.ReadInt(&msgid));
-    base::StringPiece payload;
-    EXPECT_TRUE(iter.ReadStringPiece(&payload));
-
-    // Include message deserialization in latency.
-    base::TimeTicks now = base::TimeTicks::Now();
-
-    if (payload == "hello") {
-      latency_tracker_.Reset();
-    } else if (payload == "quit") {
-      latency_tracker_.ShowResults();
-      base::MessageLoop::current()->QuitWhenIdle();
-      return true;
-    } else {
-      // Don't track hello and quit messages.
-      latency_tracker_.AddEvent(
-          base::TimeTicks::FromInternalValue(time_internal), now);
-    }
-
-    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
-    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    msg->WriteInt(msgid);
-    msg->WriteString(payload);
-    channel_->Send(msg);
-    return true;
-  }
-
- private:
-  Channel* channel_;
-  EventTimeTracker latency_tracker_;
-};
-
-// This class locks the current thread to a particular CPU core. This is
-// important because otherwise the different threads and processes of these
-// tests end up on different CPU cores which means that all of the cores are
-// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
-// frequency, leading to unpredictable and often poor performance.
-class LockThreadAffinity {
- public:
-  explicit LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) {
-#if defined(OS_WIN)
-    const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
-    old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
-    affinity_set_ok_ = old_affinity_ != 0;
-#elif defined(OS_LINUX)
-    cpu_set_t cpuset;
-    CPU_ZERO(&cpuset);
-    CPU_SET(cpu_number, &cpuset);
-    auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-    DCHECK_EQ(0, get_result);
-    auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
-    // Check for get_result failure, even though it should always succeed.
-    affinity_set_ok_ = (set_result == 0) && (get_result == 0);
-#endif
-    if (!affinity_set_ok_)
-      LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
-  }
-
-  ~LockThreadAffinity() {
-    if (!affinity_set_ok_)
-      return;
-#if defined(OS_WIN)
-    auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
-    DCHECK_NE(0u, set_result);
-#elif defined(OS_LINUX)
-    auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-    DCHECK_EQ(0, set_result);
-#endif
-  }
-
- private:
-  bool affinity_set_ok_;
-#if defined(OS_WIN)
-  DWORD_PTR old_affinity_;
-#elif defined(OS_LINUX)
-  cpu_set_t old_cpuset_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
-};
-
-class PingPongTestParams {
- public:
-  PingPongTestParams(size_t size, int count)
-      : message_size_(size), message_count_(count) {}
-
-  size_t message_size() const { return message_size_; }
-  int message_count() const { return message_count_; }
-
- private:
-  size_t message_size_;
-  int message_count_;
-};
-
-std::vector<PingPongTestParams> GetDefaultTestParams() {
-// Test several sizes. We use 12^N for message size, and limit the message
-// count to keep the test duration reasonable.
-#ifdef NDEBUG
-  const int kMultiplier = 100;
-#else
-  // Debug builds on Windows run these tests orders of magnitude more slowly.
-  const int kMultiplier = 1;
-#endif
-  std::vector<PingPongTestParams> list;
-  list.push_back(PingPongTestParams(12, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(144, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(1728, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(20736, 120 * kMultiplier));
-  list.push_back(PingPongTestParams(248832, 10 * kMultiplier));
-  return list;
-}
-
-// Avoid core 0 due to conflicts with Intel's Power Gadget.
-// Setting thread affinity will fail harmlessly on single/dual core machines.
-const int kSharedCore = 2;
-
-class MojoChannelPerfTest : public IPCChannelMojoTestBase {
- public:
-  MojoChannelPerfTest() = default;
-  ~MojoChannelPerfTest() override = default;
-
-  void RunTestChannelPingPong() {
-    Init("MojoPerfTestClient");
-
-    // Set up IPC channel and start client.
-    PerformanceChannelListener listener("Channel");
-    CreateChannel(&listener);
-    listener.Init(channel());
-    ASSERT_TRUE(ConnectChannel());
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    for (size_t i = 0; i < params.size(); i++) {
-      listener.SetTestParams(params[i].message_count(),
-                             params[i].message_size());
-
-      // This initial message will kick-start the ping-pong of messages.
-      Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-      message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-      message->WriteInt(-1);
-      message->WriteString("hello");
-      sender()->Send(message);
-
-      // Run message loop.
-      base::RunLoop().Run();
-    }
-
-    // Send quit message.
-    Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    message->WriteInt(-1);
-    message->WriteString("quit");
-    sender()->Send(message);
-
-    EXPECT_TRUE(WaitForClientShutdown());
-    DestroyChannel();
-  }
-
-  void RunTestChannelProxyPingPong() {
-    io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
-
-    Init("MojoPerfTestClient");
-
-    // Set up IPC channel and start client.
-    PerformanceChannelListener listener("ChannelProxy");
-    auto channel_proxy = IPC::ChannelProxy::Create(
-        TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
-        io_thread_->task_runner());
-    listener.Init(channel_proxy.get());
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    for (size_t i = 0; i < params.size(); i++) {
-      listener.SetTestParams(params[i].message_count(),
-                             params[i].message_size());
-
-      // This initial message will kick-start the ping-pong of messages.
-      Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-      message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-      message->WriteInt(-1);
-      message->WriteString("hello");
-      channel_proxy->Send(message);
-
-      // Run message loop.
-      base::RunLoop().Run();
-    }
-
-    // Send quit message.
-    Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    message->WriteInt(-1);
-    message->WriteString("quit");
-    channel_proxy->Send(message);
-
-    EXPECT_TRUE(WaitForClientShutdown());
-    channel_proxy.reset();
-
-    io_thread_.reset();
-  }
-
-  scoped_refptr<base::TaskRunner> io_task_runner() {
-    if (io_thread_)
-      return io_thread_->task_runner();
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
- private:
-  std::unique_ptr<base::TestIOThread> io_thread_;
-};
-
-TEST_F(MojoChannelPerfTest, ChannelPingPong) {
-  RunTestChannelPingPong();
-
-  base::RunLoop run_loop;
-  run_loop.RunUntilIdle();
-}
-
-TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) {
-  RunTestChannelProxyPingPong();
-
-  base::RunLoop run_loop;
-  run_loop.RunUntilIdle();
-}
-
-// Test to see how many channels we can create.
-TEST_F(MojoChannelPerfTest, DISABLED_MaxChannelCount) {
-#if defined(OS_POSIX)
-  LOG(INFO) << "base::GetMaxFds " << base::GetMaxFds();
-  base::SetFdLimit(20000);
-#endif
-
-  std::vector<mojo::edk::PlatformChannelPair*> channels;
-  for (size_t i = 0; i < 10000; ++i) {
-    LOG(INFO) << "channels size: " << channels.size();
-    channels.push_back(new mojo::edk::PlatformChannelPair());
-  }
-}
-
-class MojoPerfTestClient {
- public:
-  MojoPerfTestClient() : listener_(new ChannelReflectorListener()) {
-    mojo::edk::test::MultiprocessTestHelper::ChildSetup();
-  }
-
-  ~MojoPerfTestClient() = default;
-
-  int Run(MojoHandle handle) {
-    handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::unique_ptr<Channel> channel = ChannelMojo::Create(
-        std::move(handle_), Channel::MODE_CLIENT, listener_.get());
-    listener_->Init(channel.get());
-    CHECK(channel->Connect());
-
-    base::RunLoop().Run();
-    return 0;
-  }
-
- private:
-  base::MessageLoopForIO main_message_loop_;
-  std::unique_ptr<ChannelReflectorListener> listener_;
-  std::unique_ptr<Channel> channel_;
-  mojo::ScopedMessagePipeHandle handle_;
-};
-
-MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
-  MojoPerfTestClient client;
-  int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
-      base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
-      true /* pass_pipe_ownership_to_main */);
-
-  base::RunLoop run_loop;
-  run_loop.RunUntilIdle();
-
-  return rv;
-}
-
-class ReflectorImpl : public IPC::mojom::Reflector {
- public:
-  explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle)
-      : binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}
-  ~ReflectorImpl() override {
-    ignore_result(binding_.Unbind().PassMessagePipe().release());
-  }
-
- private:
-  // IPC::mojom::Reflector:
-  void Ping(const std::string& value, PingCallback callback) override {
-    std::move(callback).Run(value);
-  }
-
-  void Quit() override { base::MessageLoop::current()->QuitWhenIdle(); }
-
-  mojo::Binding<IPC::mojom::Reflector> binding_;
-};
-
-class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {
- public:
-  MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}
-
- protected:
-  void RunPingPongServer(MojoHandle mp, const std::string& label) {
-    label_ = label;
-
-    mojo::MessagePipeHandle mp_handle(mp);
-    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
-    ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    for (size_t i = 0; i < params.size(); i++) {
-      ping_receiver_->Ping("hello", base::Bind(&MojoInterfacePerfTest::OnPong,
-                                               base::Unretained(this)));
-      message_count_ = count_down_ = params[i].message_count();
-      payload_ = std::string(params[i].message_size(), 'a');
-
-      base::RunLoop().Run();
-    }
-
-    ping_receiver_->Quit();
-
-    ignore_result(ping_receiver_.PassInterface().PassHandle().release());
-  }
-
-  void OnPong(const std::string& value) {
-    if (value == "hello") {
-      DCHECK(!perf_logger_.get());
-      std::string test_name =
-          base::StringPrintf("IPC_%s_Perf_%dx_%zu", label_.c_str(),
-                             message_count_, payload_.size());
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-    } else {
-      DCHECK_EQ(payload_.size(), value.size());
-
-      CHECK(count_down_ > 0);
-      count_down_--;
-      if (count_down_ == 0) {
-        perf_logger_.reset();
-        base::MessageLoop::current()->QuitWhenIdle();
-        return;
-      }
-    }
-
-    ping_receiver_->Ping(payload_, base::Bind(&MojoInterfacePerfTest::OnPong,
-                                              base::Unretained(this)));
-  }
-
-  static int RunPingPongClient(MojoHandle mp) {
-    mojo::MessagePipeHandle mp_handle(mp);
-    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
-
-    // In single process mode, this is running in a task and by default other
-    // tasks (in particular, the binding) won't run. To keep the single process
-    // and multi-process code paths the same, enable nestable tasks.
-    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
-        base::MessageLoop::current());
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    ReflectorImpl impl(std::move(scoped_mp));
-    base::RunLoop().Run();
-    return 0;
-  }
-
- private:
-  int message_count_;
-  int count_down_;
-  std::string label_;
-  std::string payload_;
-  IPC::mojom::ReflectorPtr ping_receiver_;
-  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
-
-  DISALLOW_COPY_AND_ASSIGN(MojoInterfacePerfTest);
-};
-
-enum class InProcessMessageMode {
-  kSerialized,
-  kUnserialized,
-};
-
-class MojoInProcessInterfacePerfTest
-    : public MojoInterfacePerfTest,
-      public testing::WithParamInterface<InProcessMessageMode> {
- public:
-  MojoInProcessInterfacePerfTest() {
-    switch (GetParam()) {
-      case InProcessMessageMode::kSerialized:
-        mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
-            mojo::Connector::OutgoingSerializationMode::kEager,
-            mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
-        break;
-      case InProcessMessageMode::kUnserialized:
-        mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
-            mojo::Connector::OutgoingSerializationMode::kLazy,
-            mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
-        break;
-    }
-  }
-};
-
-DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoInterfacePerfTest, h) {
-  base::MessageLoop main_message_loop;
-  return RunPingPongClient(h);
-}
-
-// Similar to MojoChannelPerfTest above, but uses a Mojo interface instead of
-// raw IPC::Messages.
-TEST_F(MojoInterfacePerfTest, MultiprocessPingPong) {
-  RunTestClient("PingPongClient", [&](MojoHandle h) {
-    base::MessageLoop main_message_loop;
-    RunPingPongServer(h, "Multiprocess");
-  });
-}
-
-// A single process version of the above test.
-TEST_P(MojoInProcessInterfacePerfTest, MultiThreadPingPong) {
-  MojoHandle server_handle, client_handle;
-  CreateMessagePipe(&server_handle, &client_handle);
-
-  base::Thread client_thread("PingPongClient");
-  client_thread.Start();
-  client_thread.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle));
-
-  base::MessageLoop main_message_loop;
-  RunPingPongServer(server_handle, "SingleProcess");
-}
-
-TEST_P(MojoInProcessInterfacePerfTest, SingleThreadPingPong) {
-  MojoHandle server_handle, client_handle;
-  CreateMessagePipe(&server_handle, &client_handle);
-
-  base::MessageLoop main_message_loop;
-  mojo::MessagePipeHandle mp_handle(client_handle);
-  mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
-  LockThreadAffinity thread_locker(kSharedCore);
-  ReflectorImpl impl(std::move(scoped_mp));
-
-  RunPingPongServer(server_handle, "SingleProcess");
-}
-
-INSTANTIATE_TEST_CASE_P(,
-                        MojoInProcessInterfacePerfTest,
-                        testing::Values(InProcessMessageMode::kSerialized,
-                                        InProcessMessageMode::kUnserialized));
-
-class CallbackPerfTest : public testing::Test {
- public:
-  CallbackPerfTest()
-      : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {}
-
- protected:
-  void RunMultiThreadPingPongServer() {
-    client_thread_.Start();
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    for (size_t i = 0; i < params.size(); i++) {
-      std::string hello("hello");
-      client_thread_.task_runner()->PostTask(
-          FROM_HERE,
-          base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), hello));
-      message_count_ = count_down_ = params[i].message_count();
-      payload_ = std::string(params[i].message_size(), 'a');
-
-      base::RunLoop().Run();
-    }
-  }
-
-  void Ping(const std::string& value) {
-    main_message_loop_.task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&CallbackPerfTest::OnPong, base::Unretained(this), value));
-  }
-
-  void OnPong(const std::string& value) {
-    if (value == "hello") {
-      DCHECK(!perf_logger_.get());
-      std::string test_name =
-          base::StringPrintf("Callback_MultiProcess_Perf_%dx_%zu",
-                             message_count_, payload_.size());
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-    } else {
-      DCHECK_EQ(payload_.size(), value.size());
-
-      CHECK(count_down_ > 0);
-      count_down_--;
-      if (count_down_ == 0) {
-        perf_logger_.reset();
-        base::MessageLoop::current()->QuitWhenIdle();
-        return;
-      }
-    }
-
-    client_thread_.task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), payload_));
-  }
-
-  void RunSingleThreadNoPostTaskPingPongServer() {
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    base::Callback<void(const std::string&,
-                        const base::Callback<void(const std::string&)>&)>
-        ping = base::Bind(&CallbackPerfTest::SingleThreadPingNoPostTask,
-                          base::Unretained(this));
-    for (size_t i = 0; i < params.size(); i++) {
-      payload_ = std::string(params[i].message_size(), 'a');
-      std::string test_name =
-          base::StringPrintf("Callback_SingleThreadPostTask_Perf_%dx_%zu",
-                             params[i].message_count(), payload_.size());
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-      for (int j = 0; j < params[i].message_count(); ++j) {
-        ping.Run(payload_,
-                 base::Bind(&CallbackPerfTest::SingleThreadPongNoPostTask,
-                            base::Unretained(this)));
-      }
-      perf_logger_.reset();
-    }
-  }
-
-  void SingleThreadPingNoPostTask(
-      const std::string& value,
-      const base::Callback<void(const std::string&)>& pong) {
-    pong.Run(value);
-  }
-
-  void SingleThreadPongNoPostTask(const std::string& value) {}
-
-  void RunSingleThreadPostTaskPingPongServer() {
-    LockThreadAffinity thread_locker(kSharedCore);
-    std::vector<PingPongTestParams> params = GetDefaultTestParams();
-    for (size_t i = 0; i < params.size(); i++) {
-      std::string hello("hello");
-      base::MessageLoop::current()->task_runner()->PostTask(
-          FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,
-                                base::Unretained(this), hello));
-      message_count_ = count_down_ = params[i].message_count();
-      payload_ = std::string(params[i].message_size(), 'a');
-
-      base::RunLoop().Run();
-    }
-  }
-
-  void SingleThreadPingPostTask(const std::string& value) {
-    base::MessageLoop::current()->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPongPostTask,
-                              base::Unretained(this), value));
-  }
-
-  void SingleThreadPongPostTask(const std::string& value) {
-    if (value == "hello") {
-      DCHECK(!perf_logger_.get());
-      std::string test_name =
-          base::StringPrintf("Callback_SingleThreadNoPostTask_Perf_%dx_%zu",
-                             message_count_, payload_.size());
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-    } else {
-      DCHECK_EQ(payload_.size(), value.size());
-
-      CHECK(count_down_ > 0);
-      count_down_--;
-      if (count_down_ == 0) {
-        perf_logger_.reset();
-        base::MessageLoop::current()->QuitWhenIdle();
-        return;
-      }
-    }
-
-    base::MessageLoop::current()->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,
-                              base::Unretained(this), payload_));
-  }
-
- private:
-  base::Thread client_thread_;
-  base::MessageLoop main_message_loop_;
-  int message_count_;
-  int count_down_;
-  std::string payload_;
-  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
-
-  DISALLOW_COPY_AND_ASSIGN(CallbackPerfTest);
-};
-
-// Sends the same data as above using PostTask to a different thread instead of
-// IPCs for comparison.
-TEST_F(CallbackPerfTest, MultiThreadPingPong) {
-  RunMultiThreadPingPongServer();
-}
-
-// Sends the same data as above using PostTask to the same thread.
-TEST_F(CallbackPerfTest, SingleThreadPostTaskPingPong) {
-  RunSingleThreadPostTaskPingPongServer();
-}
-
-// Sends the same data as above without using PostTask to the same thread.
-TEST_F(CallbackPerfTest, SingleThreadNoPostTaskPingPong) {
-  RunSingleThreadNoPostTaskPingPongServer();
-}
-
-}  // namespace
-}  // namespace IPC
+// Copyright 2014 The Chromium Authors. All rights reserved.

+// Use of this source code is governed by a BSD-style license that can be

+// found in the LICENSE file.

+

+#include <stddef.h>

+#include <memory>

+

+#include "base/memory/ptr_util.h"

+#include "base/message_loop/message_loop.h"

+#include "base/process/process_metrics.h"

+#include "base/run_loop.h"

+#include "base/strings/stringprintf.h"

+#include "base/test/perf_time_logger.h"

+#include "base/test/test_io_thread.h"

+#include "base/threading/thread_task_runner_handle.h"

+#include "build/build_config.h"

+#include "ipc/ipc_channel_mojo.h"

+#include "ipc/ipc_test.mojom.h"

+#include "ipc/ipc_test_base.h"

+#include "mojo/edk/embedder/embedder.h"

+#include "mojo/edk/embedder/platform_channel_pair.h"

+#include "mojo/edk/test/mojo_test_base.h"

+#include "mojo/edk/test/multiprocess_test_helper.h"

+#include "mojo/public/cpp/bindings/binding.h"

+#include "mojo/public/cpp/system/message_pipe.h"

+

+#define IPC_MESSAGE_IMPL

+#include "ipc/ipc_message_macros.h"

+

+#define IPC_MESSAGE_START TestMsgStart

+

+IPC_MESSAGE_CONTROL0(TestMsg_Hello)

+IPC_MESSAGE_CONTROL0(TestMsg_Quit)

+IPC_MESSAGE_CONTROL1(TestMsg_Ping, std::string)

+IPC_SYNC_MESSAGE_CONTROL1_1(TestMsg_SyncPing, std::string, std::string)

+

+namespace IPC {

+namespace {

+

+class PerformanceChannelListener : public Listener {

+ public:

+  explicit PerformanceChannelListener(const std::string& label)

+      : label_(label),

+        sender_(NULL),

+        msg_count_(0),

+        msg_size_(0),

+        sync_(false),

+        count_down_(0) {

+    VLOG(1) << "Server listener up";

+  }

+

+  ~PerformanceChannelListener() override { VLOG(1) << "Server listener down"; }

+

+  void Init(Sender* sender) {

+    DCHECK(!sender_);

+    sender_ = sender;

+  }

+

+  // Call this before running the message loop.

+  void SetTestParams(int msg_count, size_t msg_size, bool sync) {

+    DCHECK_EQ(0, count_down_);

+    msg_count_ = msg_count;

+    msg_size_ = msg_size;

+    sync_ = sync;

+    count_down_ = msg_count_;

+    payload_ = std::string(msg_size_, 'a');

+  }

+

+  bool OnMessageReceived(const Message& message) override {

+    CHECK(sender_);

+

+    bool handled = true;

+    IPC_BEGIN_MESSAGE_MAP(PerformanceChannelListener, message)

+      IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)

+      IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)

+      IPC_MESSAGE_UNHANDLED(handled = false)

+    IPC_END_MESSAGE_MAP()

+    return handled;

+  }

+

+  void OnHello() {

+    // Start timing on hello.

+    DCHECK(!perf_logger_.get());

+    std::string test_name =

+        base::StringPrintf("IPC_%s_Perf_%dx_%u", label_.c_str(), msg_count_,

+                           static_cast<unsigned>(msg_size_));

+    perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));

+    if (sync_) {

+      for (int i = 0; i < count_down_; ++i) {

+        std::string response;

+        sender_->Send(new TestMsg_SyncPing(payload_, &response));

+      }

+      base::MessageLoop::current()->QuitWhenIdle();

+    } else {

+      SendPong();

+    }

+  }

+

+  void OnPing(const std::string& payload) {

+    // Include message deserialization in latency.

+    DCHECK_EQ(payload_.size(), payload.size());

+

+    CHECK(count_down_ > 0);

+    count_down_--;

+    if (count_down_ == 0) {

+      perf_logger_.reset();  // Stop the perf timer now.

+      base::MessageLoop::current()->QuitWhenIdle();

+      return;

+    }

+

+    SendPong();

+  }

+

+  void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }

+

+ private:

+  std::string label_;

+  Sender* sender_;

+  int msg_count_;

+  size_t msg_size_;

+  bool sync_;

+

+  int count_down_;

+  std::string payload_;

+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;

+};

+

+// This channel listener just replies to all messages with the exact same

+// message. It assumes each message has one string parameter. When the string

+// "quit" is sent, it will exit.

+class ChannelReflectorListener : public Listener {

+ public:

+  ChannelReflectorListener() : channel_(NULL) {

+    VLOG(1) << "Client listener up";

+  }

+

+  ~ChannelReflectorListener() override { VLOG(1) << "Client listener down"; }

+

+  void Init(Sender* channel) {

+    DCHECK(!channel_);

+    channel_ = channel;

+  }

+

+  bool OnMessageReceived(const Message& message) override {

+    CHECK(channel_);

+    bool handled = true;

+    IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message)

+      IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)

+      IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)

+      IPC_MESSAGE_HANDLER(TestMsg_SyncPing, OnSyncPing)

+      IPC_MESSAGE_HANDLER(TestMsg_Quit, OnQuit)

+      IPC_MESSAGE_UNHANDLED(handled = false)

+    IPC_END_MESSAGE_MAP()

+    return handled;

+  }

+

+  void OnHello() { channel_->Send(new TestMsg_Hello); }

+

+  void OnPing(const std::string& payload) {

+    channel_->Send(new TestMsg_Ping(payload));

+  }

+

+  void OnSyncPing(const std::string& payload, std::string* response) {

+    *response = payload;

+  }

+

+  void OnQuit() { base::MessageLoop::current()->QuitWhenIdle(); }

+

+  void Send(IPC::Message* message) { channel_->Send(message); }

+

+ private:

+  Sender* channel_;

+};

+

+// This class locks the current thread to a particular CPU core. This is

+// important because otherwise the different threads and processes of these

+// tests end up on different CPU cores which means that all of the cores are

+// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU

+// frequency, leading to unpredictable and often poor performance.

+class LockThreadAffinity {

+ public:

+  explicit LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) {

+#if defined(OS_WIN)

+    const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;

+    old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);

+    affinity_set_ok_ = old_affinity_ != 0;

+#elif defined(OS_LINUX)

+    cpu_set_t cpuset;

+    CPU_ZERO(&cpuset);

+    CPU_SET(cpu_number, &cpuset);

+    auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);

+    DCHECK_EQ(0, get_result);

+    auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);

+    // Check for get_result failure, even though it should always succeed.

+    affinity_set_ok_ = (set_result == 0) && (get_result == 0);

+#endif

+    if (!affinity_set_ok_)

+      LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;

+  }

+

+  ~LockThreadAffinity() {

+    if (!affinity_set_ok_)

+      return;

+#if defined(OS_WIN)

+    auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);

+    DCHECK_NE(0u, set_result);

+#elif defined(OS_LINUX)

+    auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);

+    DCHECK_EQ(0, set_result);

+#endif

+  }

+

+ private:

+  bool affinity_set_ok_;

+#if defined(OS_WIN)

+  DWORD_PTR old_affinity_;

+#elif defined(OS_LINUX)

+  cpu_set_t old_cpuset_;

+#endif

+

+  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);

+};

+

+class PingPongTestParams {

+ public:

+  PingPongTestParams(size_t size, int count)

+      : message_size_(size), message_count_(count) {}

+

+  size_t message_size() const { return message_size_; }

+  int message_count() const { return message_count_; }

+

+ private:

+  size_t message_size_;

+  int message_count_;

+};

+

+std::vector<PingPongTestParams> GetDefaultTestParams() {

+// Test several sizes. We use 12^N for message size, and limit the message

+// count to keep the test duration reasonable.

+#ifdef NDEBUG

+  const int kMultiplier = 100;

+#else

+  // Debug builds on Windows run these tests orders of magnitude more slowly.

+  const int kMultiplier = 1;

+#endif

+  std::vector<PingPongTestParams> list;

+  list.push_back(PingPongTestParams(12, 500 * kMultiplier));

+  list.push_back(PingPongTestParams(144, 500 * kMultiplier));

+  list.push_back(PingPongTestParams(1728, 500 * kMultiplier));

+  list.push_back(PingPongTestParams(20736, 120 * kMultiplier));

+  list.push_back(PingPongTestParams(248832, 10 * kMultiplier));

+  return list;

+}

+

+// Avoid core 0 due to conflicts with Intel's Power Gadget.

+// Setting thread affinity will fail harmlessly on single/dual core machines.

+const int kSharedCore = 2;

+

+class MojoChannelPerfTest : public IPCChannelMojoTestBase {

+ public:

+  MojoChannelPerfTest() = default;

+  ~MojoChannelPerfTest() override = default;

+

+  void RunTestChannelProxyPingPong() {

+    io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));

+

+    Init("MojoPerfTestClient");

+

+    // Set up IPC channel and start client.

+    PerformanceChannelListener listener("ChannelProxy");

+    auto channel_proxy = IPC::ChannelProxy::Create(

+        TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,

+        io_thread_->task_runner());

+    listener.Init(channel_proxy.get());

+

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    for (size_t i = 0; i < params.size(); i++) {

+      listener.SetTestParams(params[i].message_count(),

+                             params[i].message_size(), false);

+

+      // This initial message will kick-start the ping-pong of messages.

+      channel_proxy->Send(new TestMsg_Hello);

+

+      // Run message loop.

+      base::RunLoop().Run();

+    }

+

+    // Send quit message.

+    channel_proxy->Send(new TestMsg_Quit);

+

+    EXPECT_TRUE(WaitForClientShutdown());

+    channel_proxy.reset();

+

+    io_thread_.reset();

+  }

+

+  void RunTestChannelProxySyncPing() {

+    io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));

+

+    Init("MojoPerfTestClient");

+

+    // Set up IPC channel and start client.

+    PerformanceChannelListener listener("ChannelProxy");

+    auto channel_proxy = IPC::ChannelProxy::Create(

+        TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,

+        io_thread_->task_runner());

+    listener.Init(channel_proxy.get());

+

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    for (size_t i = 0; i < params.size(); i++) {

+      listener.SetTestParams(params[i].message_count(),

+                             params[i].message_size(), true);

+

+      // This initial message will kick-start the ping-pong of messages.

+      channel_proxy->Send(new TestMsg_Hello);

+

+      // Run message loop.

+      base::RunLoop().Run();

+    }

+

+    // Send quit message.

+    channel_proxy->Send(new TestMsg_Quit);

+

+    EXPECT_TRUE(WaitForClientShutdown());

+    channel_proxy.reset();

+

+    io_thread_.reset();

+  }

+

+  scoped_refptr<base::TaskRunner> io_task_runner() {

+    if (io_thread_)

+      return io_thread_->task_runner();

+    return base::ThreadTaskRunnerHandle::Get();

+  }

+

+ private:

+  std::unique_ptr<base::TestIOThread> io_thread_;

+};

+

+TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) {

+  RunTestChannelProxyPingPong();

+

+  base::RunLoop run_loop;

+  run_loop.RunUntilIdle();

+}

+

+TEST_F(MojoChannelPerfTest, ChannelProxySyncPing) {

+  RunTestChannelProxySyncPing();

+

+  base::RunLoop run_loop;

+  run_loop.RunUntilIdle();

+}

+

+class MojoPerfTestClient {

+ public:

+  MojoPerfTestClient() : listener_(new ChannelReflectorListener()) {

+    mojo::edk::test::MultiprocessTestHelper::ChildSetup();

+  }

+

+  ~MojoPerfTestClient() = default;

+

+  int Run(MojoHandle handle) {

+    handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));

+    LockThreadAffinity thread_locker(kSharedCore);

+    base::TestIOThread io_thread(base::TestIOThread::kAutoStart);

+

+    std::unique_ptr<ChannelProxy> channel =

+        IPC::ChannelProxy::Create(handle_.release(), Channel::MODE_CLIENT,

+                                  listener_.get(), io_thread.task_runner());

+    listener_->Init(channel.get());

+

+    base::RunLoop().Run();

+    return 0;

+  }

+

+ private:

+  base::MessageLoop main_message_loop_;

+  std::unique_ptr<ChannelReflectorListener> listener_;

+  std::unique_ptr<Channel> channel_;

+  mojo::ScopedMessagePipeHandle handle_;

+};

+

+MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {

+  MojoPerfTestClient client;

+  int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(

+      base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),

+      true /* pass_pipe_ownership_to_main */);

+

+  base::RunLoop run_loop;

+  run_loop.RunUntilIdle();

+

+  return rv;

+}

+

+class ReflectorImpl : public IPC::mojom::Reflector {

+ public:

+  explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle)

+      : binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}

+  ~ReflectorImpl() override {

+    ignore_result(binding_.Unbind().PassMessagePipe().release());

+  }

+

+ private:

+  // IPC::mojom::Reflector:

+  void Ping(const std::string& value, PingCallback callback) override {

+    std::move(callback).Run(value);

+  }

+

+  void SyncPing(const std::string& value, PingCallback callback) override {

+    std::move(callback).Run(value);

+  }

+

+  void Quit() override { base::MessageLoop::current()->QuitWhenIdle(); }

+

+  mojo::Binding<IPC::mojom::Reflector> binding_;

+};

+

+class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {

+ public:

+  MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}

+

+ protected:

+  void RunPingPongServer(MojoHandle mp, const std::string& label) {

+    label_ = label;

+

+    mojo::MessagePipeHandle mp_handle(mp);

+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);

+    ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));

+

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    for (size_t i = 0; i < params.size(); i++) {

+      ping_receiver_->Ping("hello", base::Bind(&MojoInterfacePerfTest::OnPong,

+                                               base::Unretained(this)));

+      message_count_ = count_down_ = params[i].message_count();

+      payload_ = std::string(params[i].message_size(), 'a');

+

+      base::RunLoop().Run();

+    }

+

+    ping_receiver_->Quit();

+

+    ignore_result(ping_receiver_.PassInterface().PassHandle().release());

+  }

+

+  void OnPong(const std::string& value) {

+    if (value == "hello") {

+      DCHECK(!perf_logger_.get());

+      std::string test_name =

+          base::StringPrintf("IPC_%s_Perf_%dx_%zu", label_.c_str(),

+                             message_count_, payload_.size());

+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));

+    } else {

+      DCHECK_EQ(payload_.size(), value.size());

+

+      CHECK(count_down_ > 0);

+      count_down_--;

+      if (count_down_ == 0) {

+        perf_logger_.reset();

+        base::MessageLoop::current()->QuitWhenIdle();

+        return;

+      }

+    }

+

+    if (sync_) {

+      for (int i = 0; i < count_down_; ++i) {

+        std::string response;

+        ping_receiver_->SyncPing(payload_, &response);

+      }

+      base::MessageLoop::current()->QuitWhenIdle();

+    } else {

+      ping_receiver_->Ping(payload_, base::Bind(&MojoInterfacePerfTest::OnPong,

+                                                base::Unretained(this)));

+    }

+  }

+

+  static int RunPingPongClient(MojoHandle mp) {

+    mojo::MessagePipeHandle mp_handle(mp);

+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);

+

+    // In single process mode, this is running in a task and by default other

+    // tasks (in particular, the binding) won't run. To keep the single process

+    // and multi-process code paths the same, enable nestable tasks.

+    base::MessageLoop::ScopedNestableTaskAllower nest_loop(

+        base::MessageLoop::current());

+

+    LockThreadAffinity thread_locker(kSharedCore);

+    ReflectorImpl impl(std::move(scoped_mp));

+    base::RunLoop().Run();

+    return 0;

+  }

+

+  bool sync_ = false;

+

+ private:

+  int message_count_;

+  int count_down_;

+  std::string label_;

+  std::string payload_;

+  IPC::mojom::ReflectorPtr ping_receiver_;

+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;

+

+  DISALLOW_COPY_AND_ASSIGN(MojoInterfacePerfTest);

+};

+

+enum class InProcessMessageMode {

+  kSerialized,

+  kUnserialized,

+};

+

+class MojoInProcessInterfacePerfTest

+    : public MojoInterfacePerfTest,

+      public testing::WithParamInterface<InProcessMessageMode> {

+ public:

+  MojoInProcessInterfacePerfTest() {

+    switch (GetParam()) {

+      case InProcessMessageMode::kSerialized:

+        mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(

+            mojo::Connector::OutgoingSerializationMode::kEager,

+            mojo::Connector::IncomingSerializationMode::kDispatchAsIs);

+        break;

+      case InProcessMessageMode::kUnserialized:

+        mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(

+            mojo::Connector::OutgoingSerializationMode::kLazy,

+            mojo::Connector::IncomingSerializationMode::kDispatchAsIs);

+        break;

+    }

+  }

+};

+

+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoInterfacePerfTest, h) {

+  base::MessageLoop main_message_loop;

+  return RunPingPongClient(h);

+}

+

+// Similar to MojoChannelPerfTest above, but uses a Mojo interface instead of

+// raw IPC::Messages.

+TEST_F(MojoInterfacePerfTest, MultiprocessPingPong) {

+  RunTestClient("PingPongClient", [&](MojoHandle h) {

+    base::MessageLoop main_message_loop;

+    RunPingPongServer(h, "Multiprocess");

+  });

+}

+

+TEST_F(MojoInterfacePerfTest, MultiprocessSyncPing) {

+  sync_ = true;

+  RunTestClient("PingPongClient", [&](MojoHandle h) {

+    base::MessageLoop main_message_loop;

+    RunPingPongServer(h, "MultiprocessSync");

+  });

+}

+

+// A single process version of the above test.

+TEST_P(MojoInProcessInterfacePerfTest, MultiThreadPingPong) {

+  MojoHandle server_handle, client_handle;

+  CreateMessagePipe(&server_handle, &client_handle);

+

+  base::Thread client_thread("PingPongClient");

+  client_thread.Start();

+  client_thread.task_runner()->PostTask(

+      FROM_HERE,

+      base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle));

+

+  base::MessageLoop main_message_loop;

+  RunPingPongServer(server_handle, "SingleProcess");

+}

+

+TEST_P(MojoInProcessInterfacePerfTest, SingleThreadPingPong) {

+  MojoHandle server_handle, client_handle;

+  CreateMessagePipe(&server_handle, &client_handle);

+

+  base::MessageLoop main_message_loop;

+  mojo::MessagePipeHandle mp_handle(client_handle);

+  mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);

+  LockThreadAffinity thread_locker(kSharedCore);

+  ReflectorImpl impl(std::move(scoped_mp));

+

+  RunPingPongServer(server_handle, "SingleProcess");

+}

+

+INSTANTIATE_TEST_CASE_P(,

+                        MojoInProcessInterfacePerfTest,

+                        testing::Values(InProcessMessageMode::kSerialized,

+                                        InProcessMessageMode::kUnserialized));

+

+class CallbackPerfTest : public testing::Test {

+ public:

+  CallbackPerfTest()

+      : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {}

+

+ protected:

+  void RunMultiThreadPingPongServer() {

+    client_thread_.Start();

+

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    for (size_t i = 0; i < params.size(); i++) {

+      std::string hello("hello");

+      client_thread_.task_runner()->PostTask(

+          FROM_HERE,

+          base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), hello));

+      message_count_ = count_down_ = params[i].message_count();

+      payload_ = std::string(params[i].message_size(), 'a');

+

+      base::RunLoop().Run();

+    }

+  }

+

+  void Ping(const std::string& value) {

+    main_message_loop_.task_runner()->PostTask(

+        FROM_HERE,

+        base::Bind(&CallbackPerfTest::OnPong, base::Unretained(this), value));

+  }

+

+  void OnPong(const std::string& value) {

+    if (value == "hello") {

+      DCHECK(!perf_logger_.get());

+      std::string test_name =

+          base::StringPrintf("Callback_MultiProcess_Perf_%dx_%zu",

+                             message_count_, payload_.size());

+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));

+    } else {

+      DCHECK_EQ(payload_.size(), value.size());

+

+      CHECK(count_down_ > 0);

+      count_down_--;

+      if (count_down_ == 0) {

+        perf_logger_.reset();

+        base::MessageLoop::current()->QuitWhenIdle();

+        return;

+      }

+    }

+

+    client_thread_.task_runner()->PostTask(

+        FROM_HERE,

+        base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), payload_));

+  }

+

+  void RunSingleThreadNoPostTaskPingPongServer() {

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    base::Callback<void(const std::string&,

+                        const base::Callback<void(const std::string&)>&)>

+        ping = base::Bind(&CallbackPerfTest::SingleThreadPingNoPostTask,

+                          base::Unretained(this));

+    for (size_t i = 0; i < params.size(); i++) {

+      payload_ = std::string(params[i].message_size(), 'a');

+      std::string test_name =

+          base::StringPrintf("Callback_SingleThreadPostTask_Perf_%dx_%zu",

+                             params[i].message_count(), payload_.size());

+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));

+      for (int j = 0; j < params[i].message_count(); ++j) {

+        ping.Run(payload_,

+                 base::Bind(&CallbackPerfTest::SingleThreadPongNoPostTask,

+                            base::Unretained(this)));

+      }

+      perf_logger_.reset();

+    }

+  }

+

+  void SingleThreadPingNoPostTask(

+      const std::string& value,

+      const base::Callback<void(const std::string&)>& pong) {

+    pong.Run(value);

+  }

+

+  void SingleThreadPongNoPostTask(const std::string& value) {}

+

+  void RunSingleThreadPostTaskPingPongServer() {

+    LockThreadAffinity thread_locker(kSharedCore);

+    std::vector<PingPongTestParams> params = GetDefaultTestParams();

+    for (size_t i = 0; i < params.size(); i++) {

+      std::string hello("hello");

+      base::MessageLoop::current()->task_runner()->PostTask(

+          FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,

+                                base::Unretained(this), hello));

+      message_count_ = count_down_ = params[i].message_count();

+      payload_ = std::string(params[i].message_size(), 'a');

+

+      base::RunLoop().Run();

+    }

+  }

+

+  void SingleThreadPingPostTask(const std::string& value) {

+    base::MessageLoop::current()->task_runner()->PostTask(

+        FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPongPostTask,

+                              base::Unretained(this), value));

+  }

+

+  void SingleThreadPongPostTask(const std::string& value) {

+    if (value == "hello") {

+      DCHECK(!perf_logger_.get());

+      std::string test_name =

+          base::StringPrintf("Callback_SingleThreadNoPostTask_Perf_%dx_%zu",

+                             message_count_, payload_.size());

+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));

+    } else {

+      DCHECK_EQ(payload_.size(), value.size());

+

+      CHECK(count_down_ > 0);

+      count_down_--;

+      if (count_down_ == 0) {

+        perf_logger_.reset();

+        base::MessageLoop::current()->QuitWhenIdle();

+        return;

+      }

+    }

+

+    base::MessageLoop::current()->task_runner()->PostTask(

+        FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,

+                              base::Unretained(this), payload_));

+  }

+

+ private:

+  base::Thread client_thread_;

+  base::MessageLoop main_message_loop_;

+  int message_count_;

+  int count_down_;

+  std::string payload_;

+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;

+

+  DISALLOW_COPY_AND_ASSIGN(CallbackPerfTest);

+};

+

+// Sends the same data as above using PostTask to a different thread instead of

+// IPCs for comparison.

+TEST_F(CallbackPerfTest, MultiThreadPingPong) {

+  RunMultiThreadPingPongServer();

+}

+

+// Sends the same data as above using PostTask to the same thread.

+TEST_F(CallbackPerfTest, SingleThreadPostTaskPingPong) {

+  RunSingleThreadPostTaskPingPongServer();

+}

+

+// Sends the same data as above without using PostTask to the same thread.

+TEST_F(CallbackPerfTest, SingleThreadNoPostTaskPingPong) {

+  RunSingleThreadNoPostTaskPingPongServer();

+}

+

+}  // namespace

+}  // namespace IPC

diff --git a/ipc/ipc_test.mojom b/ipc/ipc_test.mojom
index 8af397a..33181b8 100644
--- a/ipc/ipc_test.mojom
+++ b/ipc/ipc_test.mojom
@@ -31,6 +31,8 @@
 
 interface Reflector {
   Ping(string value) => (string value);
+  [Sync]
+  SyncPing(string value) => (string response);
   Quit();
 };
 
diff --git a/mash/test/mash_test_suite.cc b/mash/test/mash_test_suite.cc
index a7783011..9b0d6684 100644
--- a/mash/test/mash_test_suite.cc
+++ b/mash/test/mash_test_suite.cc
@@ -10,8 +10,8 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
-#include "cc/output/context_provider.h"
 #include "cc/surfaces/surface_manager.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "ui/aura/env.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 7ef340b6..b4cd8b5a 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -1660,4 +1660,32 @@
   CheckStreamStatusNotifications(demuxer_.get(), video_stream);
 }
 
+TEST_F(FFmpegDemuxerTest, MultitrackMemoryUsage) {
+  CreateDemuxer("multitrack-3video-2audio.webm");
+  InitializeDemuxer();
+
+  DemuxerStream* audio = GetStream(DemuxerStream::AUDIO);
+
+  // Read from the audio stream to make sure FFmpegDemuxer buffers data for all
+  // streams with available capacity, i.e all enabled streams. By default only
+  // the first audio and the first video stream are enabled, so the memory usage
+  // shouldn't be too high.
+  audio->Read(NewReadCB(FROM_HERE, 304, 0, true));
+  base::RunLoop().Run();
+  EXPECT_EQ(22134, demuxer_->GetMemoryUsage());
+
+  // Now enable all demuxer streams in the file and perform another read, this
+  // will buffer the data for additional streams and memory usage will increase.
+  std::vector<DemuxerStream*> streams = demuxer_->GetAllStreams();
+  for (auto* stream : streams)
+    static_cast<FFmpegDemuxerStream*>(stream)->SetEnabled(true,
+                                                          base::TimeDelta());
+
+  audio->Read(NewReadCB(FROM_HERE, 166, 21000, true));
+  base::RunLoop().Run();
+  // With newly enabled demuxer streams the amount of memory used by the demuxer
+  // is much higher.
+  EXPECT_EQ(156011, demuxer_->GetMemoryUsage());
+}
+
 }  // namespace media
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index 9412b79a..d1a6b1a0 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -59,10 +59,8 @@
   return g_core->CancelWatch(watcher_handle, context);
 }
 
-MojoResult MojoCreateMessageImpl(uintptr_t context,
-                                 const MojoMessageOperationThunks* thunks,
-                                 MojoMessageHandle* message) {
-  return g_core->CreateMessage(context, thunks, message);
+MojoResult MojoCreateMessageImpl(MojoMessageHandle* message) {
+  return g_core->CreateMessage(message);
 }
 
 MojoResult MojoDestroyMessageImpl(MojoMessageHandle message) {
@@ -73,6 +71,24 @@
   return g_core->SerializeMessage(message);
 }
 
+MojoResult MojoAttachSerializedMessageBufferImpl(MojoMessageHandle message,
+                                                 uint32_t payload_size,
+                                                 const MojoHandle* handles,
+                                                 uint32_t num_handles,
+                                                 void** buffer,
+                                                 uint32_t* buffer_size) {
+  return g_core->AttachSerializedMessageBuffer(
+      message, payload_size, handles, num_handles, buffer, buffer_size);
+}
+
+MojoResult MojoExtendSerializedMessagePayloadImpl(MojoMessageHandle message,
+                                                  uint32_t new_payload_size,
+                                                  void** new_buffer,
+                                                  uint32_t* new_buffer_size) {
+  return g_core->ExtendSerializedMessagePayload(message, new_payload_size,
+                                                new_buffer, new_buffer_size);
+}
+
 MojoResult MojoGetSerializedMessageContentsImpl(
     MojoMessageHandle message,
     void** buffer,
@@ -84,6 +100,14 @@
                                               handles, num_handles, flags);
 }
 
+MojoResult MojoAttachMessageContextImpl(
+    MojoMessageHandle message,
+    uintptr_t context,
+    MojoMessageContextSerializer serializer,
+    MojoMessageContextDestructor destructor) {
+  return g_core->AttachMessageContext(message, context, serializer, destructor);
+}
+
 MojoResult MojoGetMessageContextImpl(MojoMessageHandle message,
                                      uintptr_t* context,
                                      MojoGetMessageContextFlags flags) {
@@ -262,7 +286,10 @@
                                     MojoCreateMessageImpl,
                                     MojoDestroyMessageImpl,
                                     MojoSerializeMessageImpl,
+                                    MojoAttachSerializedMessageBufferImpl,
+                                    MojoExtendSerializedMessagePayloadImpl,
                                     MojoGetSerializedMessageContentsImpl,
+                                    MojoAttachMessageContextImpl,
                                     MojoGetMessageContextImpl,
                                     MojoWrapPlatformHandleImpl,
                                     MojoUnwrapPlatformHandleImpl,
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
index bfec79f..09be3b6 100644
--- a/mojo/edk/system/channel.cc
+++ b/mojo/edk/system/channel.cc
@@ -13,6 +13,7 @@
 
 #include "base/macros.h"
 #include "base/memory/aligned_memory.h"
+#include "base/numerics/safe_math.h"
 #include "base/process/process_handle.h"
 #include "mojo/edk/embedder/embedder_internal.h"
 #include "mojo/edk/embedder/platform_handle.h"
@@ -55,18 +56,30 @@
 const size_t kMaxAttachedHandles = 128;
 
 Channel::Message::Message(size_t payload_size, size_t max_handles)
-#if defined(MOJO_EDK_LEGACY_PROTOCOL)
-    : Message(payload_size, max_handles, MessageType::NORMAL_LEGACY) {
-}
-#else
-    : Message(payload_size, max_handles, MessageType::NORMAL) {
-}
-#endif
+    : Message(payload_size, payload_size, max_handles) {}
 
 Channel::Message::Message(size_t payload_size,
                           size_t max_handles,
                           MessageType message_type)
+    : Message(payload_size, payload_size, max_handles, message_type) {}
+
+Channel::Message::Message(size_t capacity,
+                          size_t payload_size,
+                          size_t max_handles)
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+    : Message(capacity, payload_size, max_handles, MessageType::NORMAL_LEGACY) {
+}
+#else
+    : Message(capacity, payload_size, max_handles, MessageType::NORMAL) {
+}
+#endif
+
+Channel::Message::Message(size_t capacity,
+                          size_t payload_size,
+                          size_t max_handles,
+                          MessageType message_type)
     : max_handles_(max_handles) {
+  DCHECK_GE(capacity, payload_size);
   DCHECK_LE(max_handles_, kMaxAttachedHandles);
 
   const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY);
@@ -94,9 +107,10 @@
       is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header);
   DCHECK(extra_header_size == 0 || !is_legacy_message);
 
+  capacity_ = header_size + extra_header_size + capacity;
   size_ = header_size + extra_header_size + payload_size;
-  data_ = static_cast<char*>(base::AlignedAlloc(size_,
-                                                kChannelMessageAlignment));
+  data_ = static_cast<char*>(
+      base::AlignedAlloc(capacity_, kChannelMessageAlignment));
   // Only zero out the header and not the payload. Since the payload is going to
   // be memcpy'd, zeroing the payload is unnecessary work and a significant
   // performance issue when dealing with large messages. Any sanitizer errors
@@ -104,11 +118,11 @@
   // treated as an error and fixed.
   memset(data_, 0, header_size + extra_header_size);
 
-  DCHECK_LE(size_, std::numeric_limits<uint32_t>::max());
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
   legacy_header()->num_bytes = static_cast<uint32_t>(size_);
 
-  DCHECK_LE(header_size + extra_header_size,
-            std::numeric_limits<uint16_t>::max());
+  DCHECK(base::IsValueInRangeForNumericType<uint16_t>(header_size +
+                                                      extra_header_size));
   legacy_header()->message_type = message_type;
 
   if (is_legacy_message) {
@@ -238,6 +252,29 @@
   return message;
 }
 
+size_t Channel::Message::capacity() const {
+  if (is_legacy_message())
+    return capacity_ - sizeof(LegacyHeader);
+  return capacity_ - header()->num_header_bytes;
+}
+
+void Channel::Message::ExtendPayload(size_t new_payload_size) {
+  size_t capacity_without_header = capacity();
+  size_t header_size = capacity_ - capacity_without_header;
+  if (new_payload_size > capacity_without_header) {
+    size_t new_capacity =
+        std::max(capacity_without_header * 2, new_payload_size) + header_size;
+    void* new_data = base::AlignedAlloc(new_capacity, kChannelMessageAlignment);
+    memcpy(new_data, data_, size_);
+    base::AlignedFree(data_);
+    data_ = static_cast<char*>(new_data);
+    capacity_ = new_capacity;
+  }
+  size_ = header_size + new_payload_size;
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
+  legacy_header()->num_bytes = static_cast<uint32_t>(size_);
+}
+
 const void* Channel::Message::extra_header() const {
   DCHECK(!is_legacy_message());
   return data_ + sizeof(Header);
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
index cc3fa96..667a00c6 100644
--- a/mojo/edk/system/channel.h
+++ b/mojo/edk/system/channel.h
@@ -124,6 +124,11 @@
     // |payload_size| bytes plus a header, plus |max_handles| platform handles.
     Message(size_t payload_size, size_t max_handles);
     Message(size_t payload_size, size_t max_handles, MessageType message_type);
+    Message(size_t capacity, size_t payload_size, size_t max_handles);
+    Message(size_t capacity,
+            size_t max_handles,
+            size_t payload_size,
+            MessageType message_type);
     ~Message();
 
     // Constructs a Message from serialized message data.
@@ -132,6 +137,18 @@
     const void* data() const { return data_; }
     size_t data_num_bytes() const { return size_; }
 
+    // The current capacity of the message buffer, not counting internal header
+    // data.
+    size_t capacity() const;
+
+    // Extends the portion of the total message capacity which contains
+    // meaningful payload data. Storage capacity which falls outside of this
+    // range is not transmitted when the message is sent.
+    //
+    // If the message's current capacity is not large enough to accommodate the
+    // new payload size, it will be reallocated accordingly.
+    void ExtendPayload(size_t new_payload_size);
+
     const void* extra_header() const;
     void* mutable_extra_header();
     size_t extra_header_size() const;
@@ -174,10 +191,20 @@
     void SetVersionForTest(uint16_t version_number);
 
    private:
-    size_t size_ = 0;
-    size_t max_handles_ = 0;
+    // The message data buffer.
     char* data_ = nullptr;
 
+    // The capacity of the buffer at |data_|.
+    size_t capacity_ = 0;
+
+    // The size of the message. This is the portion of |data_| that should
+    // be transmitted if the message is written to a channel. Includes all
+    // headers and user payload.
+    size_t size_ = 0;
+
+    // Maximum number of handles which may be attached to this message.
+    size_t max_handles_ = 0;
+
     ScopedPlatformHandleVectorPtr handle_vector_;
 
 #if defined(OS_WIN)
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 168bbf4..8b9f4c4b 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -462,24 +462,11 @@
                       ready_signals_states);
 }
 
-MojoResult Core::CreateMessage(uintptr_t context,
-                               const MojoMessageOperationThunks* thunks,
-                               MojoMessageHandle* message_handle) {
-  if (!message_handle || !context)
+MojoResult Core::CreateMessage(MojoMessageHandle* message_handle) {
+  if (!message_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
-
-  if (thunks) {
-    if (thunks->struct_size != sizeof(MojoMessageOperationThunks))
-      return MOJO_RESULT_INVALID_ARGUMENT;
-
-    if (!thunks->get_serialized_size || !thunks->serialize_handles ||
-        !thunks->serialize_payload || !thunks->destroy)
-      return MOJO_RESULT_INVALID_ARGUMENT;
-  }
-
   *message_handle = reinterpret_cast<MojoMessageHandle>(
-      UserMessageImpl::CreateEventForNewMessageWithContext(context, thunks)
-          .release());
+      UserMessageImpl::CreateEventForNewMessage().release());
   return MOJO_RESULT_OK;
 }
 
@@ -501,6 +488,46 @@
       ->SerializeIfNecessary();
 }
 
+MojoResult Core::AttachSerializedMessageBuffer(MojoMessageHandle message_handle,
+                                               uint32_t payload_size,
+                                               const MojoHandle* handles,
+                                               uint32_t num_handles,
+                                               void** buffer,
+                                               uint32_t* buffer_size) {
+  if (!message_handle || (num_handles && !handles) || !buffer || !buffer_size)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
+                      ->GetMessage<UserMessageImpl>();
+  MojoResult rv = message->AttachSerializedMessageBuffer(payload_size, handles,
+                                                         num_handles);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  *buffer = message->user_payload();
+  *buffer_size =
+      base::checked_cast<uint32_t>(message->user_payload_buffer_size());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::ExtendSerializedMessagePayload(
+    MojoMessageHandle message_handle,
+    uint32_t new_payload_size,
+    void** new_buffer,
+    uint32_t* new_buffer_size) {
+  if (!message_handle || !new_buffer || !new_buffer_size)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
+                      ->GetMessage<UserMessageImpl>();
+  MojoResult rv = message->ExtendSerializedMessagePayload(new_payload_size);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  *new_buffer = message->user_payload();
+  *new_buffer_size =
+      base::checked_cast<uint32_t>(message->user_payload_buffer_size());
+  return MOJO_RESULT_OK;
+}
+
 MojoResult Core::GetSerializedMessageContents(
     MojoMessageHandle message_handle,
     void** buffer,
@@ -546,6 +573,17 @@
       UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
 }
 
+MojoResult Core::AttachMessageContext(MojoMessageHandle message_handle,
+                                      uintptr_t context,
+                                      MojoMessageContextSerializer serializer,
+                                      MojoMessageContextDestructor destructor) {
+  if (!message_handle || !context)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
+                      ->GetMessage<UserMessageImpl>();
+  return message->AttachContext(context, serializer, destructor);
+}
+
 MojoResult Core::GetMessageContext(MojoMessageHandle message_handle,
                                    uintptr_t* context,
                                    MojoGetMessageContextFlags flags) {
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index ba98af5c..4b936a5 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -191,11 +191,19 @@
                         uintptr_t* ready_contexts,
                         MojoResult* ready_results,
                         MojoHandleSignalsState* ready_signals_states);
-  MojoResult CreateMessage(uintptr_t context,
-                           const MojoMessageOperationThunks* thunks,
-                           MojoMessageHandle* message_handle);
+  MojoResult CreateMessage(MojoMessageHandle* message_handle);
   MojoResult DestroyMessage(MojoMessageHandle message_handle);
   MojoResult SerializeMessage(MojoMessageHandle message_handle);
+  MojoResult AttachSerializedMessageBuffer(MojoMessageHandle message_handle,
+                                           uint32_t payload_size,
+                                           const MojoHandle* handles,
+                                           uint32_t num_handles,
+                                           void** buffer,
+                                           uint32_t* buffer_size);
+  MojoResult ExtendSerializedMessagePayload(MojoMessageHandle message_handle,
+                                            uint32_t new_payload_size,
+                                            void** new_buffer,
+                                            uint32_t* new_buffer_size);
   MojoResult GetSerializedMessageContents(
       MojoMessageHandle message_handle,
       void** buffer,
@@ -203,6 +211,10 @@
       MojoHandle* handles,
       uint32_t* num_handles,
       MojoGetSerializedMessageContentsFlags flags);
+  MojoResult AttachMessageContext(MojoMessageHandle message_handle,
+                                  uintptr_t context,
+                                  MojoMessageContextSerializer serializer,
+                                  MojoMessageContextDestructor destructor);
   MojoResult GetMessageContext(MojoMessageHandle message_handle,
                                uintptr_t* context,
                                MojoGetMessageContextFlags flags);
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 159a47cc..f4747b05 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -30,28 +30,6 @@
 
 using CoreTest = test::CoreTestBase;
 
-void UnusedGetSerializedSize(uintptr_t context,
-                             size_t* num_bytes,
-                             size_t* num_handles) {}
-void UnusedSerializeHandles(uintptr_t context, MojoHandle* handles) {}
-void UnusedSerializePayload(uintptr_t context, void* buffer) {}
-void IgnoreDestroyMessage(uintptr_t context) {}
-
-const MojoMessageOperationThunks kUnusedMessageThunks = {
-    sizeof(MojoMessageOperationThunks),
-    &UnusedGetSerializedSize,
-    &UnusedSerializeHandles,
-    &UnusedSerializePayload,
-    &IgnoreDestroyMessage,
-};
-
-MojoMessageHandle CreateTestMessageHandle(Core* core, uintptr_t context) {
-  MojoMessageHandle handle;
-  CHECK_EQ(MOJO_RESULT_OK,
-           core->CreateMessage(context, &kUnusedMessageThunks, &handle));
-  return handle;
-}
-
 TEST_F(CoreTest, GetTimeTicksNow) {
   const MojoTimeTicks start = core()->GetTimeTicksNow();
   ASSERT_NE(static_cast<MojoTimeTicks>(0), start)
@@ -73,10 +51,9 @@
 
   ASSERT_EQ(0u, info.GetWriteMessageCallCount());
   MojoMessageHandle message;
-  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(42, nullptr, &message));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(&message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h, CreateTestMessageHandle(core(), 42),
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+            core()->WriteMessage(h, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
   ASSERT_EQ(1u, info.GetWriteMessageCallCount());
 
   ASSERT_EQ(0u, info.GetReadMessageCallCount());
@@ -196,8 +173,10 @@
 
   // Write to |h[1]|.
   const uintptr_t kTestMessageContext = 123;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(&message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+            core()->AttachMessageContext(message, kTestMessageContext, nullptr,
+                                         nullptr));
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->WriteMessage(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
@@ -222,8 +201,10 @@
   ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
 
   // Write to |h[0]|.
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(&message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+            core()->AttachMessageContext(message, kTestMessageContext, nullptr,
+                                         nullptr));
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->WriteMessage(h[0], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
@@ -259,8 +240,10 @@
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[1].satisfiable_signals);
 
   // Try writing to |h[1]|.
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(&message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+            core()->AttachMessageContext(message, kTestMessageContext, nullptr,
+                                         nullptr));
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
             core()->WriteMessage(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
@@ -277,8 +260,10 @@
   // Make sure that |h_passing[]| work properly.
   const uintptr_t kTestMessageContext = 42;
   MojoMessageHandle message;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(&message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+            core()->AttachMessageContext(message, kTestMessageContext, nullptr,
+                                         nullptr));
   ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessage(h_passing[0], message,
                                                  MOJO_WRITE_MESSAGE_FLAG_NONE));
   hss = kEmptyMojoHandleSignalsState;
diff --git a/mojo/edk/system/message_unittest.cc b/mojo/edk/system/message_unittest.cc
index c02c1ff..8e2b109 100644
--- a/mojo/edk/system/message_unittest.cc
+++ b/mojo/edk/system/message_unittest.cc
@@ -8,6 +8,8 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
+#include "base/numerics/safe_math.h"
+#include "base/rand_util.h"
 #include "mojo/edk/test/mojo_test_base.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
@@ -26,16 +28,16 @@
 
   static MojoMessageHandle MakeMessageHandle(
       std::unique_ptr<TestMessageBase> message) {
-    MojoMessageOperationThunks thunks;
-    thunks.struct_size = sizeof(MojoMessageOperationThunks);
-    thunks.get_serialized_size = &CallGetSerializedSize;
-    thunks.serialize_handles = &CallSerializeHandles;
-    thunks.serialize_payload = &CallSerializePayload;
-    thunks.destroy = &Destroy;
     MojoMessageHandle handle;
-    MojoResult rv = MojoCreateMessage(
-        reinterpret_cast<uintptr_t>(message.release()), &thunks, &handle);
+    MojoResult rv = MojoCreateMessage(&handle);
     DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+    rv = MojoAttachMessageContext(
+        handle, reinterpret_cast<uintptr_t>(message.release()),
+        &TestMessageBase::SerializeMessageContext,
+        &TestMessageBase::DestroyMessageContext);
+    DCHECK_EQ(MOJO_RESULT_OK, rv);
+
     return handle;
   }
 
@@ -58,22 +60,28 @@
   virtual void SerializePayload(void* buffer) = 0;
 
  private:
-  static void CallGetSerializedSize(uintptr_t context,
-                                    size_t* num_bytes,
-                                    size_t* num_handles) {
-    reinterpret_cast<TestMessageBase*>(context)->GetSerializedSize(num_bytes,
-                                                                   num_handles);
+  static void SerializeMessageContext(MojoMessageHandle message_handle,
+                                      uintptr_t context) {
+    auto* message = reinterpret_cast<TestMessageBase*>(context);
+    size_t num_bytes = 0;
+    size_t num_handles = 0;
+    message->GetSerializedSize(&num_bytes, &num_handles);
+    std::vector<MojoHandle> handles(num_handles);
+    if (num_handles)
+      message->SerializeHandles(handles.data());
+
+    void* buffer;
+    uint32_t buffer_size;
+    MojoResult rv = MojoAttachSerializedMessageBuffer(
+        message_handle, base::checked_cast<uint32_t>(num_bytes), handles.data(),
+        base::checked_cast<uint32_t>(num_handles), &buffer, &buffer_size);
+    DCHECK_EQ(MOJO_RESULT_OK, rv);
+    DCHECK_GE(buffer_size, base::checked_cast<uint32_t>(num_bytes));
+    if (num_bytes)
+      message->SerializePayload(buffer);
   }
 
-  static void CallSerializeHandles(uintptr_t context, MojoHandle* handles) {
-    reinterpret_cast<TestMessageBase*>(context)->SerializeHandles(handles);
-  }
-
-  static void CallSerializePayload(uintptr_t context, void* buffer) {
-    reinterpret_cast<TestMessageBase*>(context)->SerializePayload(buffer);
-  }
-
-  static void Destroy(uintptr_t context) {
+  static void DestroyMessageContext(uintptr_t context) {
     delete reinterpret_cast<TestMessageBase*>(context);
   }
 };
@@ -156,10 +164,24 @@
             MojoSerializeMessage(MOJO_MESSAGE_HANDLE_INVALID));
 
   MojoMessageHandle message_handle;
+  void* buffer;
+  uint32_t buffer_size;
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCreateMessage(nullptr));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message_handle));
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoCreateMessage(0, nullptr, &message_handle));
+            MojoAttachMessageContext(message_handle, 0, nullptr, nullptr));
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoCreateMessage(1234, nullptr, nullptr));
+            MojoAttachSerializedMessageBuffer(message_handle, 0, nullptr, 0,
+                                              &buffer, nullptr));
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoAttachSerializedMessageBuffer(message_handle, 0, nullptr, 0,
+                                              nullptr, &buffer_size));
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoAttachSerializedMessageBuffer(message_handle, 0, nullptr, 0,
+                                              nullptr, nullptr));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoAttachSerializedMessageBuffer(message_handle, 0, nullptr, 0,
+                                              &buffer, &buffer_size));
 }
 
 TEST_F(MessageTest, SendLocalMessageWithContext) {
@@ -221,6 +243,25 @@
   });
 }
 
+TEST_F(MessageTest, SerializeDynamicallySizedMessage) {
+  RunTestClient("ReceiveMessageNoHandles", [&](MojoHandle h) {
+    MojoMessageHandle message;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+
+    void* buffer;
+    uint32_t buffer_size;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoAttachSerializedMessageBuffer(message, 0, nullptr, 0, &buffer,
+                                                &buffer_size));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoExtendSerializedMessagePayload(
+                                  message, sizeof(kTestMessageWithContext1) - 1,
+                                  &buffer, &buffer_size));
+    memcpy(buffer, kTestMessageWithContext1,
+           sizeof(kTestMessageWithContext1) - 1);
+    MojoWriteMessage(h, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
+  });
+}
+
 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveMessageOneHandle, MessageTest, h) {
   MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE);
   MojoHandle h1;
@@ -491,6 +532,178 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle));
 }
 
+TEST_F(MessageTest, ExtendMessagePayload) {
+  MojoMessageHandle message;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+
+  const std::string kTestMessagePart1("hello i am message.");
+  void* buffer;
+  uint32_t buffer_size;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoAttachSerializedMessageBuffer(
+                message, static_cast<uint32_t>(kTestMessagePart1.size()),
+                nullptr, 0, &buffer, &buffer_size));
+  ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessagePart1.size()));
+  memcpy(buffer, kTestMessagePart1.data(), kTestMessagePart1.size());
+
+  void* payload;
+  uint32_t payload_size;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &payload, &payload_size, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(kTestMessagePart1.size(), payload_size);
+  EXPECT_EQ(
+      0, memcmp(payload, kTestMessagePart1.data(), kTestMessagePart1.size()));
+
+  const std::string kTestMessagePart2 = " in ur computer.";
+  const std::string kTestMessageCombined1 =
+      kTestMessagePart1 + kTestMessagePart2;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoExtendSerializedMessagePayload(
+                message, static_cast<uint32_t>(kTestMessageCombined1.size()),
+                &buffer, &buffer_size));
+  memcpy(static_cast<uint8_t*>(buffer) + kTestMessagePart1.size(),
+         kTestMessagePart2.data(), kTestMessagePart2.size());
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &payload, &payload_size, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(kTestMessageCombined1.size(), payload_size);
+  EXPECT_EQ(0, memcmp(payload, kTestMessageCombined1.data(),
+                      kTestMessageCombined1.size()));
+
+  const std::string kTestMessagePart3 = kTestMessagePart2 + " carry ur bits.";
+  const std::string kTestMessageCombined2 =
+      kTestMessageCombined1 + kTestMessagePart3;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoExtendSerializedMessagePayload(
+                message, static_cast<uint32_t>(kTestMessageCombined2.size()),
+                &buffer, &buffer_size));
+  memcpy(static_cast<uint8_t*>(buffer) + kTestMessageCombined1.size(),
+         kTestMessagePart3.data(), kTestMessagePart3.size());
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &payload, &payload_size, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(kTestMessageCombined2.size(), payload_size);
+  EXPECT_EQ(0, memcmp(payload, kTestMessageCombined2.data(),
+                      kTestMessageCombined2.size()));
+}
+
+TEST_F(MessageTest, ExtendMessageWithHandlesPayload) {
+  MojoMessageHandle message;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+
+  MojoHandle handles[2];
+  CreateMessagePipe(&handles[0], &handles[1]);
+
+  const std::string kTestMessagePart1("hello i am message.");
+  void* buffer;
+  uint32_t buffer_size;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoAttachSerializedMessageBuffer(
+                message, static_cast<uint32_t>(kTestMessagePart1.size()),
+                handles, 2, &buffer, &buffer_size));
+  ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessagePart1.size()));
+  memcpy(buffer, kTestMessagePart1.data(), kTestMessagePart1.size());
+
+  const std::string kTestMessagePart2 = " in ur computer.";
+  const std::string kTestMessageCombined1 =
+      kTestMessagePart1 + kTestMessagePart2;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoExtendSerializedMessagePayload(
+                message, static_cast<uint32_t>(kTestMessageCombined1.size()),
+                &buffer, &buffer_size));
+  memcpy(static_cast<uint8_t*>(buffer) + kTestMessagePart1.size(),
+         kTestMessagePart2.data(), kTestMessagePart2.size());
+
+  void* payload;
+  uint32_t payload_size;
+  uint32_t num_handles = 2;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &payload, &payload_size, handles, &num_handles,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(2u, num_handles);
+  EXPECT_EQ(kTestMessageCombined1.size(), payload_size);
+  EXPECT_EQ(0, memcmp(payload, kTestMessageCombined1.data(),
+                      kTestMessageCombined1.size()));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[0]));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[1]));
+}
+
+TEST_F(MessageTest, ExtendMessagePayloadLarge) {
+  // We progressively extend a message payload from small to large using various
+  // chunk sizes to test potentially interesting boundary conditions.
+  constexpr size_t kTestChunkSizes[] = {1, 2, 3, 64, 509, 4096, 16384, 65535};
+  for (const size_t kChunkSize : kTestChunkSizes) {
+    MojoMessageHandle message;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+
+    MojoHandle handles[2];
+    CreateMessagePipe(&handles[0], &handles[1]);
+
+    const std::string kTestMessageHeader("hey pretend i'm a header");
+    void* buffer;
+    uint32_t buffer_size;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoAttachSerializedMessageBuffer(
+                  message, static_cast<uint32_t>(kTestMessageHeader.size()),
+                  handles, 2, &buffer, &buffer_size));
+    ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessageHeader.size()));
+    memcpy(buffer, kTestMessageHeader.data(), kTestMessageHeader.size());
+
+    // 5 MB should be well beyond any reasonable default buffer size for the
+    // system implementation to choose, meaning that this test should guarantee
+    // several reallocations of the serialized message buffer as we
+    // progressively extend the payload to this size.
+    constexpr size_t kTestMessagePayloadSize = 5 * 1024 * 1024;
+    std::vector<uint8_t> test_payload(kTestMessagePayloadSize);
+    base::RandBytes(test_payload.data(), kTestMessagePayloadSize);
+
+    size_t current_payload_size = 0;
+    while (current_payload_size < kTestMessagePayloadSize) {
+      const size_t previous_payload_size = current_payload_size;
+      current_payload_size =
+          std::min(current_payload_size + kChunkSize, kTestMessagePayloadSize);
+      const size_t current_chunk_size =
+          current_payload_size - previous_payload_size;
+      const size_t previous_total_size =
+          kTestMessageHeader.size() + previous_payload_size;
+      const size_t current_total_size =
+          kTestMessageHeader.size() + current_payload_size;
+      EXPECT_EQ(MOJO_RESULT_OK,
+                MojoExtendSerializedMessagePayload(
+                    message, static_cast<uint32_t>(current_total_size), &buffer,
+                    &buffer_size));
+      EXPECT_GE(buffer_size, static_cast<uint32_t>(current_total_size));
+      memcpy(static_cast<uint8_t*>(buffer) + previous_total_size,
+             &test_payload[previous_payload_size], current_chunk_size);
+    }
+
+    void* payload;
+    uint32_t payload_size;
+    uint32_t num_handles = 2;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoGetSerializedMessageContents(
+                  message, &payload, &payload_size, handles, &num_handles,
+                  MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+    EXPECT_EQ(static_cast<uint32_t>(kTestMessageHeader.size() +
+                                    kTestMessagePayloadSize),
+              payload_size);
+    EXPECT_EQ(0, memcmp(payload, kTestMessageHeader.data(),
+                        kTestMessageHeader.size()));
+    EXPECT_EQ(0,
+              memcmp(static_cast<uint8_t*>(payload) + kTestMessageHeader.size(),
+                     test_payload.data(), kTestMessagePayloadSize));
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[0]));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[1]));
+  }
+}
+
 }  // namespace
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index 7e7bdc2..1c4f327 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/request_context.h"
 
@@ -127,9 +128,15 @@
 Channel::MessagePtr CreateMessage(MessageType type,
                                   size_t payload_size,
                                   size_t num_handles,
-                                  DataType** out_data) {
-  Channel::MessagePtr message(
-      new Channel::Message(sizeof(Header) + payload_size, num_handles));
+                                  DataType** out_data,
+                                  size_t capacity = 0) {
+  const size_t total_size = payload_size + sizeof(Header);
+  if (capacity == 0)
+    capacity = total_size;
+  else
+    capacity = std::max(total_size, capacity);
+  auto message =
+      base::MakeUnique<Channel::Message>(capacity, total_size, num_handles);
   Header* header = reinterpret_cast<Header*>(message->mutable_payload());
   header->type = type;
   header->padding = 0;
@@ -167,11 +174,12 @@
 }
 
 // static
-Channel::MessagePtr NodeChannel::CreateEventMessage(size_t payload_size,
+Channel::MessagePtr NodeChannel::CreateEventMessage(size_t capacity,
+                                                    size_t payload_size,
                                                     void** payload,
                                                     size_t num_handles) {
   return CreateMessage(MessageType::EVENT_MESSAGE, payload_size, num_handles,
-                       payload);
+                       payload, capacity);
 }
 
 // static
diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h
index c962c6b..b3eac19 100644
--- a/mojo/edk/system/node_channel.h
+++ b/mojo/edk/system/node_channel.h
@@ -95,7 +95,8 @@
       scoped_refptr<base::TaskRunner> io_task_runner,
       const ProcessErrorCallback& process_error_callback);
 
-  static Channel::MessagePtr CreateEventMessage(size_t payload_size,
+  static Channel::MessagePtr CreateEventMessage(size_t capacity,
+                                                size_t payload_size,
                                                 void** payload,
                                                 size_t num_handles);
 
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
index 865ebb6..f0ca64b 100644
--- a/mojo/edk/system/node_controller.cc
+++ b/mojo/edk/system/node_controller.cc
@@ -85,8 +85,8 @@
   }
 
   void* data;
-  auto message =
-      NodeChannel::CreateEventMessage(event->GetSerializedSize(), &data, 0);
+  size_t size = event->GetSerializedSize();
+  auto message = NodeChannel::CreateEventMessage(size, size, &data, 0);
   event->Serialize(data);
   return message;
 }
diff --git a/mojo/edk/system/user_message_impl.cc b/mojo/edk/system/user_message_impl.cc
index 727a4d2..842019d 100644
--- a/mojo/edk/system/user_message_impl.cc
+++ b/mojo/edk/system/user_message_impl.cc
@@ -4,7 +4,10 @@
 
 #include "mojo/edk/system/user_message_impl.h"
 
+#include <algorithm>
+
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros_local.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "mojo/edk/embedder/embedder_internal.h"
@@ -21,6 +24,11 @@
 
 namespace {
 
+// The minimum amount of memory to allocate for a new serialized message buffer.
+// This should be sufficiently large such that most seiralized messages do not
+// incur any reallocations as they're expanded to full size.
+const uint32_t kMinimumPayloadBufferSize = 4096;
+
 #pragma pack(push, 1)
 // Header attached to every message.
 struct MessageHeader {
@@ -57,10 +65,12 @@
 MojoResult SerializeEventMessage(
     ports::UserMessageEvent* event,
     size_t payload_size,
+    size_t payload_buffer_size,
     const Dispatcher::DispatcherInTransit* dispatchers,
     size_t num_dispatchers,
     Channel::MessagePtr* out_message,
     void** out_header,
+    size_t* out_header_size,
     void** out_user_payload) {
   // A structure for tracking information about every Dispatcher that will be
   // serialized into the message. This is NOT part of the message itself.
@@ -94,9 +104,11 @@
   event->ReservePorts(num_ports);
   const size_t event_size = event->GetSerializedSize();
   const size_t total_size = event_size + header_size + payload_size;
+  const size_t total_buffer_size =
+      event_size + header_size + payload_buffer_size;
   void* data;
-  Channel::MessagePtr message =
-      NodeChannel::CreateEventMessage(total_size, &data, num_handles);
+  Channel::MessagePtr message = NodeChannel::CreateEventMessage(
+      total_buffer_size, total_size, &data, num_handles);
   auto* header = reinterpret_cast<MessageHeader*>(static_cast<uint8_t*>(data) +
                                                   event_size);
 
@@ -166,6 +178,7 @@
 
   *out_message = std::move(message);
   *out_header = header;
+  *out_header_size = header_size;
   *out_user_payload = reinterpret_cast<uint8_t*>(header) + header_size;
   return MOJO_RESULT_OK;
 }
@@ -176,11 +189,10 @@
 const ports::UserMessage::TypeInfo UserMessageImpl::kUserMessageTypeInfo = {};
 
 UserMessageImpl::~UserMessageImpl() {
-  if (HasContext()) {
-    if (context_thunks_.has_value())
-      context_thunks_->destroy(context_);
+  if (HasContext() && context_destructor_) {
     DCHECK(!channel_message_);
     DCHECK(!has_serialized_handles_);
+    context_destructor_(context_);
   } else if (IsSerialized() && has_serialized_handles_) {
     // Ensure that any handles still serialized within this message are
     // extracted and closed so they don't leak.
@@ -198,12 +210,10 @@
 
 // static
 std::unique_ptr<ports::UserMessageEvent>
-UserMessageImpl::CreateEventForNewMessageWithContext(
-    uintptr_t context,
-    const MojoMessageOperationThunks* thunks) {
+UserMessageImpl::CreateEventForNewMessage() {
   auto message_event = base::MakeUnique<ports::UserMessageEvent>(0);
-  message_event->AttachMessage(base::WrapUnique(
-      new UserMessageImpl(message_event.get(), context, thunks)));
+  message_event->AttachMessage(
+      base::WrapUnique(new UserMessageImpl(message_event.get())));
   return message_event;
 }
 
@@ -217,14 +227,15 @@
   void* header = nullptr;
   void* user_payload = nullptr;
   auto event = base::MakeUnique<ports::UserMessageEvent>(0);
-  MojoResult rv = SerializeEventMessage(event.get(), num_bytes, dispatchers,
-                                        num_dispatchers, &channel_message,
-                                        &header, &user_payload);
+  size_t header_size = 0;
+  MojoResult rv = SerializeEventMessage(
+      event.get(), num_bytes, num_bytes, dispatchers, num_dispatchers,
+      &channel_message, &header, &header_size, &user_payload);
   if (rv != MOJO_RESULT_OK)
     return rv;
   event->AttachMessage(base::WrapUnique(
       new UserMessageImpl(event.get(), std::move(channel_message), header,
-                          user_payload, num_bytes)));
+                          header_size, user_payload, num_bytes)));
   *out_event = std::move(event);
   return MOJO_RESULT_OK;
 }
@@ -248,7 +259,7 @@
   const size_t user_payload_size = payload_size - header_size;
   return base::WrapUnique(
       new UserMessageImpl(message_event, std::move(channel_message), header,
-                          user_payload, user_payload_size));
+                          header_size, user_payload, user_payload_size));
 }
 
 // static
@@ -277,58 +288,97 @@
   return context;
 }
 
+size_t UserMessageImpl::user_payload_buffer_size() const {
+  DCHECK(IsSerialized());
+  return channel_message_->capacity();
+}
+
 size_t UserMessageImpl::num_handles() const {
   DCHECK(IsSerialized());
   DCHECK(header_);
   return static_cast<const MessageHeader*>(header_)->num_dispatchers;
 }
 
-MojoResult UserMessageImpl::SerializeIfNecessary() {
+MojoResult UserMessageImpl::AttachContext(
+    uintptr_t context,
+    MojoMessageContextSerializer serializer,
+    MojoMessageContextDestructor destructor) {
+  if (HasContext())
+    return MOJO_RESULT_ALREADY_EXISTS;
   if (IsSerialized())
     return MOJO_RESULT_FAILED_PRECONDITION;
-  if (!context_thunks_.has_value())
-    return MOJO_RESULT_NOT_FOUND;
+  context_ = context;
+  context_serializer_ = serializer;
+  context_destructor_ = destructor;
+  return MOJO_RESULT_OK;
+}
 
-  DCHECK(HasContext());
-  DCHECK(!has_serialized_handles_);
-  size_t num_bytes = 0;
-  size_t num_handles = 0;
-  context_thunks_->get_serialized_size(context_, &num_bytes, &num_handles);
-
-  std::vector<ScopedHandle> handles(num_handles);
+MojoResult UserMessageImpl::AttachSerializedMessageBuffer(
+    uint32_t payload_size,
+    const MojoHandle* handles,
+    uint32_t num_handles) {
   std::vector<Dispatcher::DispatcherInTransit> dispatchers;
   if (num_handles > 0) {
-    auto* raw_handles = reinterpret_cast<MojoHandle*>(handles.data());
-    context_thunks_->serialize_handles(context_, raw_handles);
     MojoResult acquire_result = internal::g_core->AcquireDispatchersForTransit(
-        raw_handles, num_handles, &dispatchers);
+        handles, num_handles, &dispatchers);
     if (acquire_result != MOJO_RESULT_OK)
       return acquire_result;
   }
-
   Channel::MessagePtr channel_message;
   MojoResult rv = SerializeEventMessage(
-      message_event_, num_bytes, dispatchers.data(), num_handles,
-      &channel_message, &header_, &user_payload_);
+      message_event_, payload_size,
+      std::max(payload_size, kMinimumPayloadBufferSize), dispatchers.data(),
+      num_handles, &channel_message, &header_, &header_size_, &user_payload_);
   if (num_handles > 0) {
     internal::g_core->ReleaseDispatchersForTransit(dispatchers,
                                                    rv == MOJO_RESULT_OK);
   }
-
   if (rv != MOJO_RESULT_OK)
     return MOJO_RESULT_ABORTED;
-
-  // The handles are now owned by the message event. Release them here.
-  for (auto& handle : handles)
-    ignore_result(handle.release());
-
-  if (num_bytes)
-    context_thunks_->serialize_payload(context_, user_payload_);
-  user_payload_size_ = num_bytes;
+  user_payload_size_ = payload_size;
   channel_message_ = std::move(channel_message);
+  has_serialized_handles_ = true;
+  return MOJO_RESULT_OK;
+}
 
-  context_thunks_->destroy(context_);
-  context_ = 0;
+MojoResult UserMessageImpl::ExtendSerializedMessagePayload(
+    uint32_t new_payload_size) {
+  if (!IsSerialized())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  if (new_payload_size < user_payload_size_)
+    return MOJO_RESULT_OUT_OF_RANGE;
+
+  size_t header_offset =
+      static_cast<uint8_t*>(header_) -
+      static_cast<const uint8_t*>(channel_message_->payload());
+  size_t user_payload_offset =
+      static_cast<uint8_t*>(user_payload_) -
+      static_cast<const uint8_t*>(channel_message_->payload());
+  channel_message_->ExtendPayload(user_payload_offset + new_payload_size);
+  header_ = static_cast<uint8_t*>(channel_message_->mutable_payload()) +
+            header_offset;
+  user_payload_ = static_cast<uint8_t*>(channel_message_->mutable_payload()) +
+                  user_payload_offset;
+  user_payload_size_ = new_payload_size;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult UserMessageImpl::SerializeIfNecessary() {
+  if (IsSerialized())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  DCHECK(HasContext());
+  DCHECK(!has_serialized_handles_);
+  if (!context_serializer_)
+    return MOJO_RESULT_NOT_FOUND;
+
+  uintptr_t context = ReleaseContext();
+  context_serializer_(reinterpret_cast<MojoMessageHandle>(message_event_),
+                      context);
+
+  if (context_destructor_)
+    context_destructor_(context);
+
   has_serialized_handles_ = true;
   return MOJO_RESULT_OK;
 }
@@ -417,19 +467,14 @@
   return MOJO_RESULT_OK;
 }
 
-UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event,
-                                 uintptr_t context,
-                                 const MojoMessageOperationThunks* thunks)
+UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event)
     : ports::UserMessage(&kUserMessageTypeInfo),
-      message_event_(message_event),
-      context_(context) {
-  if (thunks)
-    context_thunks_ = *thunks;
-}
+      message_event_(message_event) {}
 
 UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event,
                                  Channel::MessagePtr channel_message,
                                  void* header,
+                                 size_t header_size,
                                  void* user_payload,
                                  size_t user_payload_size)
     : ports::UserMessage(&kUserMessageTypeInfo),
@@ -437,6 +482,7 @@
       channel_message_(std::move(channel_message)),
       has_serialized_handles_(true),
       header_(header),
+      header_size_(header_size),
       user_payload_(user_payload),
       user_payload_size_(user_payload_size) {}
 
diff --git a/mojo/edk/system/user_message_impl.h b/mojo/edk/system/user_message_impl.h
index 461e4e2..0d6475f 100644
--- a/mojo/edk/system/user_message_impl.h
+++ b/mojo/edk/system/user_message_impl.h
@@ -50,11 +50,8 @@
 
   ~UserMessageImpl() override;
 
-  // Creates a new ports::UserMessageEvent with an attached unserialized
-  // UserMessageImpl associated with |context| and |thunks|.
-  static std::unique_ptr<ports::UserMessageEvent>
-  CreateEventForNewMessageWithContext(uintptr_t context,
-                                      const MojoMessageOperationThunks* thunks);
+  // Creates a new ports::UserMessageEvent with an attached UserMessageImpl.
+  static std::unique_ptr<ports::UserMessageEvent> CreateEventForNewMessage();
 
   // Creates a new ports::UserMessageEvent with an attached serialized
   // UserMessageImpl. May fail iff one or more |dispatchers| fails to serialize
@@ -114,11 +111,21 @@
     return user_payload_size_;
   }
 
+  size_t user_payload_buffer_size() const;
+
   size_t num_handles() const;
 
   void set_source_node(const ports::NodeName& name) { source_node_ = name; }
   const ports::NodeName& source_node() const { return source_node_; }
 
+  MojoResult AttachContext(uintptr_t context,
+                           MojoMessageContextSerializer serializer,
+                           MojoMessageContextDestructor destructor);
+  MojoResult AttachSerializedMessageBuffer(uint32_t payload_size,
+                                           const MojoHandle* handles,
+                                           uint32_t num_handles);
+  MojoResult ExtendSerializedMessagePayload(uint32_t new_payload_size);
+
   // If this message is not already serialized, this serializes it.
   MojoResult SerializeIfNecessary();
 
@@ -140,9 +147,7 @@
   // |thunks|. If the message is ever going to be routed to another node (see
   // |WillBeRoutedExternally()| below), it will be serialized at that time using
   // operations provided by |thunks|.
-  UserMessageImpl(ports::UserMessageEvent* message_event,
-                  uintptr_t context,
-                  const MojoMessageOperationThunks* thunks);
+  UserMessageImpl(ports::UserMessageEvent* message_event);
 
   // Creates a serialized UserMessageImpl backed by an existing Channel::Message
   // object. |header| and |user_payload| must be pointers into
@@ -151,6 +156,7 @@
   UserMessageImpl(ports::UserMessageEvent* message_event,
                   Channel::MessagePtr channel_message,
                   void* header,
+                  size_t header_size,
                   void* user_payload,
                   size_t user_payload_size);
 
@@ -162,7 +168,8 @@
 
   // Unserialized message state.
   uintptr_t context_ = 0;
-  base::Optional<MojoMessageOperationThunks> context_thunks_;
+  MojoMessageContextSerializer context_serializer_ = nullptr;
+  MojoMessageContextDestructor context_destructor_ = nullptr;
 
   // Serialized message contents. May be null if this is not a serialized
   // message.
@@ -178,6 +185,7 @@
   // after any serialized dispatchers, with the payload comprising the remaining
   // |user_payload_size_| bytes of the message.
   void* header_ = nullptr;
+  size_t header_size_ = 0;
   void* user_payload_ = nullptr;
   size_t user_payload_size_ = 0;
 
diff --git a/mojo/public/c/system/README.md b/mojo/public/c/system/README.md
index a668fc1..847eb2dc 100644
--- a/mojo/public/c/system/README.md
+++ b/mojo/public/c/system/README.md
@@ -126,46 +126,75 @@
 The state of these conditions can be queried and watched asynchronously as
 described in the [Signals & Watchers](#Signals-Watchers) section below.
 
-### Allocating Messages
+### Creating Messages
 
-In order to avoid redundant internal buffer copies, Mojo would like to allocate
-your message storage buffers for you. This is easy:
+Message pipes carry message objects which may or may not be serialized. You can
+create a new message object as follows:
 
 ``` c
 MojoMessageHandle message;
-MojoResult result = MojoAllocMessage(6, NULL, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE,
-                                     &message);
+MojoResult result = MojoCreateMessage(&message);
 ```
 
 Note that we have a special `MojoMessageHandle` type for message objects.
 
-The code above allocates a buffer for a message payload of 6 bytes with no
-handles attached.
+Messages may be serialized or unserialized. Unserialized messages may support
+lazy serialization, meaning they can be serialized if and only if necessary
+(e.g. if the message needs to cross a process boundary.)
 
-If we change our mind and decide not to send this message, we can delete it:
+To make a serialized message, you might write something like:
 
 ``` c
-MojoResult result = MojoFreeMessage(message);
+void* buffer;
+uint32_t buffer_size;
+MojoResult result = MojoAttachSerializedMessageBuffer(
+    message, 6, nullptr, 0, &buffer, &buffer_size);
 ```
 
-If we instead decide to send our newly allocated message, we first need to fill
-in the payload data with something interesting. How about a pleasant greeting:
+This attaches a serialized message buffer to `message` with at least `6` bytes
+of storage capacity. The results stored in `buffer` and `buffer_size` give more
+information about the payload's storage.
+
+If you want to increase the size of the payload layer, you can use
+`MojoExtendSerializedMessagePayload`.
+
+Creating lazily-serialized messages is also straightforward:
 
 ``` c
-void* buffer = NULL;
-MojoResult result = MojoGetMessageBuffer(message, &buffer);
-memcpy(buffer, "hello", 6);
+struct MyMessage {
+  // some interesting data...
+};
+
+void SerializeMessage(MojoMessageHandle message, uintptr_t context) {
+  struct MyMessage* my_message = (struct MyMessage*)context;
+
+  MojoResult result = MojoAttachSerializedMessageBuffer(message, ...);
+  // Serialize however you like.
+
+  free(my_message);
+}
+
+MyMessage* data = malloc(sizeof(MyMessage));
+// initialize *data...
+
+MojoResult result = MojoAttachMessageContext(message, (uintptr_t)data,
+                                             &SerializeMessage);
 ```
 
-Now we can write the message to a pipe. Note that attempting to write a message
-transfers ownership of the message object (and any attached handles) into the
-target pipe and there is therefore no need to subsequently call
-`MojoFreeMessage` on that message.
+If we change our mind and decide not to send a message, we can destroy it:
+
+``` c
+MojoResult result = MojoDestroyMessage(message);
+```
+
+Note that attempting to write a message transfers ownership of the message
+object (and any attached handles) into the message pipe, and there is therefore
+no need to subsequently call `MojoDestroyMessage` on that message.
 
 ### Writing Messages
 
 ``` c
-result = MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
+result = MojoWriteMessage(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
 ```
 
 `MojoWriteMessage` is a *non-blocking* call: it always returns
@@ -180,10 +209,11 @@
 [Signals & Watchers](#Signals-Watchers) below for more information.
 
 *** aside
-**NOTE**: Although this is an implementation detail and not strictly guaranteed by the
-System API, it is true in the current implementation that the message will
-arrive at `b` before the above `MojoWriteMessage` call even returns, because `b`
-is in the same process as `a` and has never been transferred over another pipe.
+**NOTE**: Although this is an implementation detail and not strictly guaranteed
+by the System API, it is true in the current implementation that the message
+will arrive at `b` before the above `MojoWriteMessage` call even returns,
+because `b` is in the same process as `a` and has never been transferred over
+another pipe.
 ***
 
 ### Reading Messages
@@ -192,16 +222,17 @@
 
 ``` c
 MojoMessageHandle message;
-uint32_t num_bytes;
-MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, NULL, NULL,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
+MojoResult result = MojoReadMessage(b, &message, MOJO_READ_MESSAGE_FLAG_NONE);
 ```
 
-and map its buffer to retrieve the contents:
+and extract its serialized contents:
 
 ``` c
 void* buffer = NULL;
-MojoResult result = MojoGetMessageBuffer(message, &buffer);
+uint32_t num_bytes;
+MojoResult result = MojoGetSerializedMessageContents(
+    message, &buffer, &num_bytes, nullptr, nullptr,
+    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
 printf("Pipe says: %s", (const char*)buffer);
 ```
 
@@ -212,13 +243,21 @@
 
 ``` c
 MojoMessageHandle message;
-MojoResult result = MojoReadMessageNew(b, &message, NULL, NULL, NULL,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
+MojoResult result = MojoReadMessage(b, &message, MOJO_READ_MESSAGE_FLAG_NONE);
 ```
 
 We'll get a `result` of `MOJO_RESULT_SHOULD_WAIT`, indicating that the pipe is
 not yet readable.
 
+Note that message also may not have been serialized if they came from within the
+same process. In that case, `MojoGetSerializedMessageContents` will return
+`MOJO_RESULT_FAILED_PRECONDITION`. The message's unserialized context can
+instead be retrieved using `MojoGetMessageContext`.
+
+Messages read from a message pipe are owned by the caller and must be
+subsequently destroyed using `MojoDestroyMessage` (or, in theory, written to
+another pipe using `MojoWriteMessage`.)
+
 ### Messages With Handles
 
 Probably the most useful feature of Mojo IPC is that message pipes can carry
@@ -233,52 +272,53 @@
 ``` c
 MojoHandle a, b;
 MojoHandle c, d;
-MojoMessage message;
+MojoCreateMessagePipe(NULL, &a, &b);
+MojoCreateMessagePipe(NULL, &c, &d);
 
 // Allocate a message with an empty payload and handle |c| attached. Note that
 // this takes ownership of |c|, effectively invalidating its handle value.
-MojoResult result = MojoAllocMessage(0, &c, 1, MOJO_ALLOC_MESSAGE_FLAG_NONE,
-                                     message);
-
-result = MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
+MojoMessageHandle message;
+void* buffer;
+uint32_t buffer_size;
+MojoCreateMessage(&message);
+MojoAttachSerializedMessageBuffer(message, "hi", 2, &c, 1, &buffer,
+                                  &buffer_size);
+MojoWriteMessage(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
 
 // Some time later...
-uint32_t num_bytes;
 MojoHandle e;
 uint32_t num_handles = 1;
-MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, &e,
-                                       &num_handles,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
+MojoReadMessage(b, &message, MOJO_READ_MESSAGE_FLAG_NONE);
+MojoGetSerializedMessageContents(
+    message, &buffer, &buffer_size, &e, &num_handles,
+    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
 ```
 
 At this point the handle in `e` is now referencing the same message pipe
 endpoint which was originally referenced by `c`.
 
 Note that `num_handles` above is initialized to 1 before we pass its address to
-`MojoReadMessageNew`. This is to indicate how much `MojoHandle` storage is
-available at the output buffer we gave it (`&e` above).
+`MojoGetSerializedMessageContents`. This is to indicate how much `MojoHandle`
+storage is available at the output buffer we gave it (`&e` above).
 
 If we didn't know how many handles to expect in an incoming message -- which is
-often the case -- we can use `MojoReadMessageNew` to query for this information
-first:
+often the case -- we can use `MojoGetSerializedMessageContents` to query for
+this information first:
 
 ``` c
 MojoMessageHandle message;
+void* buffer;
 uint32_t num_bytes = 0;
 uint32_t num_handles = 0;
-MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, NULL,
-                                       &num_handles,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
+MojoResult result = MojoGetSerializedMessageContents(
+    message, &buffer, &num_bytes, NULL, &num_handles,
+    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
 ```
 
-If in this case there were a received message on `b` with some nonzero number
-of handles, `result` would be `MOJO_RESULT_RESOURCE_EXHAUSTED`, and both
-`num_bytes` and `num_handles` would be updated to reflect the payload size and
-number of attached handles on the next available message.
-
-It's also worth noting that if there did happen to be a message available with
-no payload and no handles (*i.e.* an empty message), this would actually return
-`MOJO_RESULT_OK`.
+If `message` has some non-zero number of handles, `result` will be
+`MOJO_RESULT_RESOURCE_EXHAUSTED`, and both `num_bytes` and `num_handles` would
+be updated to reflect the payload size and number of attached handles in the
+message.
 
 ## Data Pipes
 
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
index d83155a..90d55253 100644
--- a/mojo/public/c/system/message_pipe.h
+++ b/mojo/public/c/system/message_pipe.h
@@ -110,55 +110,23 @@
 extern "C" {
 #endif
 
-// A structure used with MojoCreateMessage to inform the system about how to
-// operate on an opaque message object given by |context|.
+// A callback which can serialize a message given some context. Passed to
+// MojoAttachMessageContext along with a context it knows how to serialize.
+// See |MojoAttachMessageContext()| for more details.
 //
-// See |MojoCreateMessage()| for more details.
-#pragma pack(push, 8)
-struct MojoMessageOperationThunks {
-  // Must be set to sizeof(MojoMessageOperationThunks).
-  size_t struct_size;
+//   |message| is a message object which had |context| attached.
+//   |context| the context which was attached to |message|.
+//
+// Note that the context is always detached from the message immediately before
+// this callback is invoked, and that the associated destructor (if any) is also
+// invoked on |context| immediately after the serializer returns.
+typedef void (*MojoMessageContextSerializer)(MojoMessageHandle message,
+                                             uintptr_t context);
 
-  // Computes the number of bytes of payload and the number of handles required
-  // to serialize the message associated with |context|. If the message needs
-  // to be serialized, this is the first function invoked by the system,
-  // followed immediately by |serialize_handles()| or |serialize_payload()| for
-  // the same |context|.
-  //
-  // The implementation must populate |*num_bytes| and |*num_handles|.
-  void (*get_serialized_size)(uintptr_t context,
-                              size_t* num_bytes,
-                              size_t* num_handles);
-
-  // Passes ownership of any MojoHandles carried by the message associated with
-  // |context|. Called immediately after |get_serialized_size()| for the same
-  // |context|, if and only if the returned |*num_handles| was non-zero.
-  //
-  // The implementation must populate |handles| with a contiguous MojoHandle
-  // array of exactly |*num_handles| (from above) elements.
-  void (*serialize_handles)(uintptr_t context, MojoHandle* handles);
-
-  // Serializes the message payload bytes into a message buffer. This always
-  // follows a call to |get_serialized_size()| (and possibly
-  // |serialize_handles()|) for the same |context|, if and only if |*num_bytes|
-  // (from above) is non-zero.
-  //
-  // The implementation must populate |buffer| with exactly |*num_bytes| bytes
-  // of message content. The storage in |buffer| is NOT initialized otherwise,
-  // so it is the caller's responsibility to ensure that ALL |*num_bytes| bytes
-  // are written to.
-  void (*serialize_payload)(uintptr_t context, void* buffer);
-
-  // Destroys the message object associated with |context|. This is called when
-  // the message associated with |context| is either explicitly freed by
-  // |MojoDestroyMessage()| or serialized for transmission across a process
-  // boundary (after serialization is complete.)
-  //
-  // The implementation must use this to release any resources associated with
-  // |context|.
-  void (*destroy)(uintptr_t context);
-};
-#pragma pack(pop)
+// A callback which can be used to destroy a message context after serialization
+// or in the event that the message to which it's attached is destroyed without
+// ever being serialized.
+typedef void (*MojoMessageContextDestructor)(uintptr_t context);
 
 // Note: See the comment in functions.h about the meaning of the "optional"
 // label for pointer parameters.
@@ -185,8 +153,8 @@
 
 // Writes a message to the message pipe endpoint given by |message_pipe_handle|.
 //
-// Note that regardless of success or failure, |message| is freed by this call
-// and therefore invalidated.
+// Note that regardless of success or failure, |message| is destroyed by this
+// call and therefore invalidated.
 //
 // Returns:
 //   |MOJO_RESULT_OK| on success (i.e., the message was enqueued).
@@ -196,6 +164,8 @@
 //       Note that closing an endpoint is not necessarily synchronous (e.g.,
 //       across processes), so this function may succeed even if the other
 //       endpoint has been closed (in which case the message would be dropped).
+//   |MOJO_RESULT_NOT_FOUND| if |message| has neither a context nor serialized
+//       buffer attached and therefore has nothing to be written.
 MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
                                                MojoMessageHandle message,
                                                MojoWriteMessageFlags flags);
@@ -247,37 +217,16 @@
 // |MojoWriteMessage()|. Returns a handle to the new message object in
 // |*message|.
 //
-// |context| is an arbitrary context value to associate with this message, and
-// |thunks| is a set of functions which may be called on |context| to perform
-// various operations. See |MojoMessageOperationThunks| for details.
-//
-// Typically a caller will use |context| as an opaque pointer to some heap
-// object which is effectively owned by the newly created message once this
-// returns. In this way, messages can be sent over a message pipe to a peer
-// endpoint in the same process as the sender without ever performing a
-// serialization step.
-//
-// If the message does need to cross a process boundary or is otherwise
-// forced to serialize (see |MojoSerializeMessage()| below), it will be
-// serialized by invoking the serialization helper functions from |thunks| on
-// |context| to obtain a serialized representation of the message. Note that
-// because the need to serialize may occur at any time and on any thread,
-// functions in |thunks| must be safe to invoke accordingly.
-//
-// If |thunks| is null, the created message cannot be serialized. Subsequent
-// calls to |MojoSerializeMessage()| on the created message, or any attempt to
-// transmit the message across a process boundary, will fail.
+// In its initial state the message object cannot be successfully written to a
+// message pipe, but must first have either a context or serialized buffer
+// attached (see |MojoAttachContext()| and |MojoAttachSerializedMessageBuffer()|
+// below.)
 //
 // Returns:
 //   |MOJO_RESULT_OK| if a new message was created. |*message| contains a handle
-//       to the new message object on return.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if |context| is 0, or |thunks| is non-null
-//       and any element of |thunks| is invalid. In this case, the value of
-//       |message| is ignored.
-MOJO_SYSTEM_EXPORT MojoResult
-MojoCreateMessage(uintptr_t context,
-                  const struct MojoMessageOperationThunks* thunks,
-                  MojoMessageHandle* message);
+//       to the new message object upon return.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is null.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessage(MojoMessageHandle* message);
 
 // Destroys a message object created by |MojoCreateMessage()| or
 // |MojoReadMessage()|.
@@ -300,7 +249,7 @@
 //       written to a pipe or decomposed by MojoGetSerializedMessageContents().
 //   |MOJO_RESULT_FAILED_PRECONDITION| if |message| was already serialized.
 //   |MOJO_RESULT_NOT_FOUND| if |message| cannot be serialized (i.e. it was
-//       created with null |MojoMessageOperationThunks|.)
+//       created with null |MojoMessageContextSerializer|.)
 //   |MOJO_RESULT_BUSY| if one or more handles provided by the user context
 //       reported itself as busy during the serialization attempt. In this case
 //       all serialized handles are closed automatically.
@@ -314,6 +263,77 @@
 // allows callers to coerce eager serialization.
 MOJO_SYSTEM_EXPORT MojoResult MojoSerializeMessage(MojoMessageHandle message);
 
+// Attaches a serialized message buffer to a message object.
+//
+// |message|: The message to which a buffer is to be attached.
+// |payload_size|: The initial expected payload size of the message. If this
+//     call succeeds, the attached buffer will be at least this large.
+// |handles|: The handles to attach to the serialized message. The set of
+//     attached handles may not be augmented once the buffer is attached, and so
+//     all handle attachments must be known up front. May be null if
+//     |num_handles| is 0.
+// |num_handles|: The number of handles provided by |handles|.
+//
+// Note that while a serialized message buffer's size may exceed the size of the
+// payload, when a serialized message is transmitted (or its contents retrieved
+// with |MojoGetSerializedMessageContents()|), only the extent of the payload
+// is transmitted and/or exposed. Use |MojoExtendSerializedMessagePayload()| to
+// extend the portion of the buffer which is designated as valid payload and
+// (if necessary) expand the available capacity.
+//
+// Ownership of all handles in |handles| is tranferred to the message object if
+// and ONLY if this operation succeeds and returns |MOJO_RESULT_OK|. Otherwise
+// the caller retains ownership.
+//
+// Returns:
+//   |MOJO_RESULT_OK| upon success. A new serialized message buffer has been
+//       attached to |message|. The address of the buffer's storage is output in
+//       |*buffer| and its size is in |*buffer_size|.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object;
+//       if |num_handles| is non-zero but |handles| is null; if either
+//       |buffer| or |buffer_size| is null; or if any handle in |handles| is
+//       invalid.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if |payload_size| or |num_handles| exceeds
+//       some implementation- or embedder-defined maximum.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if |message| has a context attached.
+//   |MOJO_RESULT_ALREADY_EXISTS| if |message| already has a serialized buffer
+//       attached.
+//   |MOJO_RESULT_BUSY| if one or more handles in |handles| is currently busy
+//       and unable to be serialized.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoAttachSerializedMessageBuffer(MojoMessageHandle message,
+                                  uint32_t payload_size,
+                                  const MojoHandle* handles,
+                                  uint32_t num_handles,
+                                  void** buffer,
+                                  uint32_t* buffer_size);
+
+// Extends the designated payload size within a serialized message buffer. If
+// the buffer is not large enough to accomodate the additional payload size,
+// this will attempt to expand the buffer before returning.
+//
+// |message|: The message getting additional payload.
+// |new_payload_size|: The new total of the payload. Must be at least as large
+//     as the message's current payload size.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the new payload size has been committed to the message.
+//       If necessary, the message's buffer may have been reallocated to
+//       accommodate additional capacity. |*buffer| and |*buffer_size| contain
+//       the (possibly changed) address and size of the serialized message
+//       buffer's storage.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object,
+//       or either |buffer| or |buffer_size| is null.
+//   |MOJO_RESULT_OUT_OF_RANGE| if |payload_size| is not at least as large as
+//       the message's current payload size.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if |message| does not have a serialized
+//       message buffer attached.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoExtendSerializedMessagePayload(MojoMessageHandle message,
+                                   uint32_t new_payload_size,
+                                   void** new_buffer,
+                                   uint32_t* new_buffer_size);
+
 // Retrieves the contents of a serialized message.
 //
 // |message|: The message whose contents are to be retrieved.
@@ -365,20 +385,60 @@
                                  uint32_t* num_handles,
                                  MojoGetSerializedMessageContentsFlags flags);
 
+// Attachs an unserialized message context to a message.
+//
+// |context| is the context value to associate with this message, and
+// |serializer| is a function which may be called at some later time to convert
+// |message| to a serialized message object using |context| as input. See
+// |MojoMessageContextSerializer| for more details. |destructor| is a function
+// which may be called to allow the user to cleanup state associated with
+// |context| after serialization or in the event that the message is destroyed
+// without ever being serialized.
+//
+// Typically a caller will use |context| as an opaque pointer to some heap
+// object which is effectively owned by the newly created message once this
+// returns. In this way, messages can be sent over a message pipe to a peer
+// endpoint in the same process as the sender without ever performing a
+// serialization step.
+//
+// If the message does need to cross a process boundary or is otherwise
+// forced to serialize (see |MojoSerializeMessage()| below), it will be
+// serialized by invoking |serializer|.
+//
+// If |serializer| is null, the created message cannot be serialized. Subsequent
+// calls to |MojoSerializeMessage()| on the created message, or any attempt to
+// transmit the message across a process boundary, will fail.
+//
+// If |destructor| is null, it is assumed that no cleanup is required after
+// serializing or destroying a message with |context| attached.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the context was successfully attached.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |context| is 0 or |message| is not a
+//       valid message object.
+//   |MOJO_RESULT_ALREADY_EXISTS| if |message| already has a context attached.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if |message| already has a serialized
+//       buffer attached.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoAttachMessageContext(MojoMessageHandle message,
+                         uintptr_t context,
+                         MojoMessageContextSerializer serializer,
+                         MojoMessageContextDestructor destructor);
+
 // Extracts the user-provided context from a message and returns it to the
-// caller. This can only succeed if the message is not in a serialized form.
+// caller.
 //
 // |flags|: Flags to alter the behavior of this call. See
 //     |MojoGetMessageContextFlags| for details.
 //
 // Returns:
-//   |MOJO_RESULT_OK| if |message| was a valid message object which has not yet
-//       been serialized. Upon return, |*context| contains the context value
-//       originally provided to the |MojoCreateMessage()| call which created
+//   |MOJO_RESULT_OK| if |message| is a valid message object which has a
+//       |context| attached. Upon return, |*context| contains the value of the
+//       attached context. If |flags| contains
+//       |MOJO_GET_MESSAGE_CONTEXT_FLAG_RELEASE|, the |context| is detached from
 //       |message|.
 //   |MOJO_RESULT_NOT_FOUND| if |message| is a valid message object which has no
-//       associated context value. In this case it must be a serialized message,
-//       and |MojoGetSerializedMessageContents()| should be called instead.
+//       attached context.
 //   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object.
 MOJO_SYSTEM_EXPORT MojoResult
 MojoGetMessageContext(MojoMessageHandle message,
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 7e02a1a..7fa87ed 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -98,8 +98,10 @@
 
   // Write to |h1|.
   const uintptr_t kTestMessageContext = 1234;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoCreateMessage(kTestMessageContext, nullptr, &message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoAttachMessageContext(message, kTestMessageContext, nullptr, nullptr));
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoWriteMessage(h1, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
index fc6d6a3d..4d7e934 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -56,7 +56,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1));
 
   MojoMessageHandle message;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(42, NULL, &message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(&message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoAttachMessageContext(message, 42, NULL, NULL));
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoWriteMessage(handle0, message, MOJO_WRITE_DATA_FLAG_NONE));
 
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index 2ce9edb..5a08dbf7 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -173,11 +173,9 @@
   return g_thunks.FuseMessagePipes(handle0, handle1);
 }
 
-MojoResult MojoCreateMessage(uintptr_t context,
-                             const MojoMessageOperationThunks* thunks,
-                             MojoMessageHandle* message) {
+MojoResult MojoCreateMessage(MojoMessageHandle* message) {
   assert(g_thunks.CreateMessage);
-  return g_thunks.CreateMessage(context, thunks, message);
+  return g_thunks.CreateMessage(message);
 }
 
 MojoResult MojoDestroyMessage(MojoMessageHandle message) {
@@ -190,6 +188,26 @@
   return g_thunks.SerializeMessage(message);
 }
 
+MojoResult MojoAttachSerializedMessageBuffer(MojoMessageHandle message,
+                                             uint32_t payload_size,
+                                             const MojoHandle* handles,
+                                             uint32_t num_handles,
+                                             void** buffer,
+                                             uint32_t* buffer_size) {
+  assert(g_thunks.AttachSerializedMessageBuffer);
+  return g_thunks.AttachSerializedMessageBuffer(
+      message, payload_size, handles, num_handles, buffer, buffer_size);
+}
+
+MojoResult MojoExtendSerializedMessagePayload(MojoMessageHandle message,
+                                              uint32_t new_payload_size,
+                                              void** buffer,
+                                              uint32_t* buffer_size) {
+  assert(g_thunks.ExtendSerializedMessagePayload);
+  return g_thunks.ExtendSerializedMessagePayload(message, new_payload_size,
+                                                 buffer, buffer_size);
+}
+
 MojoResult MojoGetSerializedMessageContents(
     MojoMessageHandle message,
     void** buffer,
@@ -202,6 +220,15 @@
                                                handles, num_handles, flags);
 }
 
+MojoResult MojoAttachMessageContext(MojoMessageHandle message,
+                                    uintptr_t context,
+                                    MojoMessageContextSerializer serializer,
+                                    MojoMessageContextDestructor destructor) {
+  assert(g_thunks.AttachMessageContext);
+  return g_thunks.AttachMessageContext(message, context, serializer,
+                                       destructor);
+}
+
 MojoResult MojoGetMessageContext(MojoMessageHandle message,
                                  uintptr_t* context,
                                  MojoGetMessageContextFlags flags) {
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
index c6d8abe82..9f40696 100644
--- a/mojo/public/c/system/thunks.h
+++ b/mojo/public/c/system/thunks.h
@@ -86,11 +86,19 @@
                            MojoResult* ready_results,
                            MojoHandleSignalsState* ready_signals_states);
   MojoResult (*FuseMessagePipes)(MojoHandle handle0, MojoHandle handle1);
-  MojoResult (*CreateMessage)(uintptr_t context,
-                              const struct MojoMessageOperationThunks* thunks,
-                              MojoMessageHandle* message);
+  MojoResult (*CreateMessage)(MojoMessageHandle* message);
   MojoResult (*DestroyMessage)(MojoMessageHandle message);
   MojoResult (*SerializeMessage)(MojoMessageHandle message);
+  MojoResult (*AttachSerializedMessageBuffer)(MojoMessageHandle message,
+                                              uint32_t payload_size,
+                                              const MojoHandle* handles,
+                                              uint32_t num_handles,
+                                              void** buffer,
+                                              uint32_t* buffer_size);
+  MojoResult (*ExtendSerializedMessagePayload)(MojoMessageHandle message,
+                                               uint32_t new_payload_size,
+                                               void** buffer,
+                                               uint32_t* buffer_size);
   MojoResult (*GetSerializedMessageContents)(
       MojoMessageHandle message,
       void** buffer,
@@ -98,6 +106,10 @@
       MojoHandle* handles,
       uint32_t* num_handles,
       MojoGetSerializedMessageContentsFlags flags);
+  MojoResult (*AttachMessageContext)(MojoMessageHandle message,
+                                     uintptr_t context,
+                                     MojoMessageContextSerializer serializer,
+                                     MojoMessageContextDestructor destructor);
   MojoResult (*GetMessageContext)(MojoMessageHandle message,
                                   uintptr_t* context,
                                   MojoGetMessageContextFlags flags);
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index a6d927c7..11b02d9fb 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/numerics/safe_math.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_local.h"
 #include "mojo/public/cpp/bindings/associated_group_controller.h"
@@ -34,47 +35,6 @@
   message.NotifyBadMessage(error);
 }
 
-// An internal serialization context used to initialize new serialized messages.
-struct MessageInfo {
-  MessageInfo(size_t total_size, std::vector<ScopedHandle>* handles)
-      : total_size(total_size), handles(handles) {}
-  const size_t total_size;
-  std::vector<ScopedHandle>* handles;
-  internal::Buffer payload_buffer;
-};
-
-void GetSerializedSizeFromMessageInfo(uintptr_t context,
-                                      size_t* num_bytes,
-                                      size_t* num_handles) {
-  auto* info = reinterpret_cast<MessageInfo*>(context);
-  *num_bytes = info->total_size;
-  *num_handles = info->handles ? info->handles->size() : 0;
-}
-
-void SerializeHandlesFromMessageInfo(uintptr_t context, MojoHandle* handles) {
-  auto* info = reinterpret_cast<MessageInfo*>(context);
-  DCHECK(info->handles);
-  for (size_t i = 0; i < info->handles->size(); ++i)
-    handles[i] = info->handles->at(i).release().value();
-}
-
-void SerializePayloadFromMessageInfo(uintptr_t context, void* storage) {
-  auto* info = reinterpret_cast<MessageInfo*>(context);
-  info->payload_buffer = internal::Buffer(storage, info->total_size);
-}
-
-void DestroyMessageInfo(uintptr_t context) {
-  // MessageInfo is always stack-allocated, so there's nothing to do here.
-}
-
-const MojoMessageOperationThunks kMessageInfoThunks{
-    sizeof(MojoMessageOperationThunks),
-    &GetSerializedSizeFromMessageInfo,
-    &SerializeHandlesFromMessageInfo,
-    &SerializePayloadFromMessageInfo,
-    &DestroyMessageInfo,
-};
-
 template <typename HeaderType>
 void AllocateHeaderFromBuffer(internal::Buffer* buffer, HeaderType** header) {
   *header = static_cast<HeaderType*>(buffer->Allocate(sizeof(HeaderType)));
@@ -118,65 +78,80 @@
                                    std::vector<ScopedHandle>* handles,
                                    ScopedMessageHandle* out_handle,
                                    internal::Buffer* out_buffer) {
-  internal::Buffer buffer;
-  MessageInfo info(internal::ComputeSerializedMessageSize(
-                       flags, payload_size, payload_interface_id_count),
-                   handles);
   ScopedMessageHandle handle;
-  MojoResult rv = mojo::CreateMessage(reinterpret_cast<uintptr_t>(&info),
-                                      &kMessageInfoThunks, &handle);
+  MojoResult rv = mojo::CreateMessage(&handle);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
   DCHECK(handle.is_valid());
 
-  // Force the message object to be serialized immediately. MessageInfo thunks
-  // are invoked here to serialize the handles and allocate enough space for
-  // header and user payload. |info.payload_buffer| is initialized with a Buffer
-  // that can be used to write the header and payload data.
-  rv = MojoSerializeMessage(handle->value());
+  void* buffer;
+  uint32_t buffer_size;
+  size_t total_size = internal::ComputeSerializedMessageSize(
+      flags, payload_size, payload_interface_id_count);
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(total_size));
+  DCHECK(!handles ||
+         base::IsValueInRangeForNumericType<uint32_t>(handles->size()));
+  rv = MojoAttachSerializedMessageBuffer(
+      handle->value(), static_cast<uint32_t>(total_size),
+      handles ? reinterpret_cast<MojoHandle*>(handles->data()) : nullptr,
+      handles ? static_cast<uint32_t>(handles->size()) : 0, &buffer,
+      &buffer_size);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
-  DCHECK(info.payload_buffer.is_valid());
+  if (handles) {
+    // Handle ownership has been taken by MojoAttachSerializedMessageBuffer.
+    for (size_t i = 0; i < handles->size(); ++i)
+      ignore_result(handles->at(i).release());
+  }
+
+  internal::Buffer payload_buffer(buffer, buffer_size);
 
   // Make sure we zero the memory first!
-  memset(info.payload_buffer.data(), 0, info.total_size);
-  WriteMessageHeader(name, flags, payload_interface_id_count,
-                     &info.payload_buffer);
+  memset(payload_buffer.data(), 0, total_size);
+  WriteMessageHeader(name, flags, payload_interface_id_count, &payload_buffer);
 
   *out_handle = std::move(handle);
-  *out_buffer = std::move(info.payload_buffer);
+  *out_buffer = std::move(payload_buffer);
 }
 
-void GetSerializedSizeFromUnserializedContext(uintptr_t context,
-                                              size_t* num_bytes,
-                                              size_t* num_handles) {
-  reinterpret_cast<internal::UnserializedMessageContext*>(context)
-      ->GetSerializedSize(num_bytes, num_handles);
-}
-
-void SerializeHandlesFromUnserializedContext(uintptr_t context,
-                                             MojoHandle* handles) {
-  reinterpret_cast<internal::UnserializedMessageContext*>(context)
-      ->SerializeHandles(handles);
-}
-
-void SerializePayloadFromUnserializedContext(uintptr_t context_value,
-                                             void* storage) {
+void SerializeUnserializedContext(MojoMessageHandle message,
+                                  uintptr_t context_value) {
   auto* context =
       reinterpret_cast<internal::UnserializedMessageContext*>(context_value);
-  internal::Buffer payload_buffer(storage, context->total_serialized_size());
+  size_t num_bytes;
+  size_t num_handles;
+  context->GetSerializedSize(&num_bytes, &num_handles);
+  if (!base::IsValueInRangeForNumericType<uint32_t>(num_bytes))
+    return;
+  if (!base::IsValueInRangeForNumericType<uint32_t>(num_handles))
+    return;
+
+  std::vector<MojoHandle> handles(num_handles);
+  if (num_handles)
+    context->SerializeHandles(handles.data());
+
+  void* buffer;
+  uint32_t buffer_size;
+  MojoResult rv = MojoAttachSerializedMessageBuffer(
+      message, static_cast<uint32_t>(num_bytes), handles.data(),
+      static_cast<uint32_t>(num_handles), &buffer, &buffer_size);
+  if (rv != MOJO_RESULT_OK)
+    return;
+
+  internal::Buffer payload_buffer(buffer, num_bytes);
   WriteMessageHeader(context->message_name(), context->message_flags(),
                      0 /* payload_interface_id_count */, &payload_buffer);
 
   // We need to copy additional header data which may have been set after
   // message construction, as this codepath may be reached at some arbitrary
   // time between message send and message dispatch.
-  static_cast<internal::MessageHeader*>(storage)->interface_id =
+  static_cast<internal::MessageHeader*>(buffer)->interface_id =
       context->header()->interface_id;
   if (context->header()->flags &
       (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
     DCHECK_GE(context->header()->version, 1u);
-    static_cast<internal::MessageHeaderV1*>(storage)->request_id =
+    static_cast<internal::MessageHeaderV1*>(buffer)->request_id =
         context->header()->request_id;
   }
+
   context->SerializePayload(&payload_buffer);
 }
 
@@ -184,22 +159,17 @@
   delete reinterpret_cast<internal::UnserializedMessageContext*>(context);
 }
 
-const MojoMessageOperationThunks kUnserializedMessageContextThunks{
-    sizeof(MojoMessageOperationThunks),
-    &GetSerializedSizeFromUnserializedContext,
-    &SerializeHandlesFromUnserializedContext,
-    &SerializePayloadFromUnserializedContext,
-    &DestroyUnserializedContext,
-};
-
 ScopedMessageHandle CreateUnserializedMessageObject(
     std::unique_ptr<internal::UnserializedMessageContext> context) {
   ScopedMessageHandle handle;
-  MojoResult rv =
-      mojo::CreateMessage(reinterpret_cast<uintptr_t>(context.release()),
-                          &kUnserializedMessageContextThunks, &handle);
+  MojoResult rv = mojo::CreateMessage(&handle);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
   DCHECK(handle.is_valid());
+
+  rv = MojoAttachMessageContext(
+      handle->value(), reinterpret_cast<uintptr_t>(context.release()),
+      &SerializeUnserializedContext, &DestroyUnserializedContext);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
   return handle;
 }
 
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
index d498f049..ed00e6a2 100644
--- a/mojo/public/cpp/bindings/tests/validation_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -12,6 +12,7 @@
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
+#include "base/numerics/safe_math.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/c/system/macros.h"
@@ -34,33 +35,20 @@
 namespace test {
 namespace {
 
-void GetSerializedRawMessageSize(uintptr_t context,
-                                 size_t* num_bytes,
-                                 size_t* num_handles) {
-  *num_bytes = *reinterpret_cast<size_t*>(context);
-  *num_handles = 0;
-}
-static void IgnoreSerializeHandles(uintptr_t, MojoHandle*) {}
-static void IgnoreSerializePayload(uintptr_t, void*) {}
-static void IgnoreDestroy(uintptr_t) {}
-
-const MojoMessageOperationThunks kRawMessageThunks{
-    sizeof(MojoMessageOperationThunks),
-    &GetSerializedRawMessageSize,
-    &IgnoreSerializeHandles,
-    &IgnoreSerializePayload,
-    &IgnoreDestroy,
-};
-
 Message CreateRawMessage(size_t size) {
   ScopedMessageHandle handle;
-  MojoResult rv = CreateMessage(reinterpret_cast<uintptr_t>(&size),
-                                &kRawMessageThunks, &handle);
+  MojoResult rv = CreateMessage(&handle);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
   DCHECK(handle.is_valid());
 
-  rv = MojoSerializeMessage(handle->value());
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size));
+  void* buffer;
+  uint32_t buffer_size;
+  rv = MojoAttachSerializedMessageBuffer(handle->value(),
+                                         static_cast<uint32_t>(size), nullptr,
+                                         0, &buffer, &buffer_size);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
+
   return Message(std::move(handle));
 }
 
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
index 13d7f85..58aed263 100644
--- a/mojo/public/cpp/system/message.h
+++ b/mojo/public/cpp/system/message.h
@@ -50,11 +50,9 @@
 
 using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
 
-inline MojoResult CreateMessage(uintptr_t context,
-                                const MojoMessageOperationThunks* thunks,
-                                ScopedMessageHandle* handle) {
+inline MojoResult CreateMessage(ScopedMessageHandle* handle) {
   MojoMessageHandle raw_handle;
-  MojoResult rv = MojoCreateMessage(context, thunks, &raw_handle);
+  MojoResult rv = MojoCreateMessage(&raw_handle);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
diff --git a/mojo/public/cpp/system/message_pipe.cc b/mojo/public/cpp/system/message_pipe.cc
index 3f66b92..f5b1c0a 100644
--- a/mojo/public/cpp/system/message_pipe.cc
+++ b/mojo/public/cpp/system/message_pipe.cc
@@ -4,82 +4,31 @@
 
 #include "mojo/public/cpp/system/message_pipe.h"
 
+#include "base/numerics/safe_math.h"
+
 namespace mojo {
 
-namespace {
-
-struct RawMessage {
-  RawMessage(const void* bytes,
-             size_t num_bytes,
-             const MojoHandle* handles,
-             size_t num_handles)
-      : bytes(static_cast<const uint8_t*>(bytes)),
-        num_bytes(num_bytes),
-        handles(handles),
-        num_handles(num_handles) {}
-
-  const uint8_t* const bytes;
-  const size_t num_bytes;
-  const MojoHandle* const handles;
-  const size_t num_handles;
-};
-
-void GetRawMessageSize(uintptr_t context,
-                       size_t* num_bytes,
-                       size_t* num_handles) {
-  auto* message = reinterpret_cast<RawMessage*>(context);
-  *num_bytes = message->num_bytes;
-  *num_handles = message->num_handles;
-}
-
-void SerializeRawMessageHandles(uintptr_t context, MojoHandle* handles) {
-  auto* message = reinterpret_cast<RawMessage*>(context);
-  DCHECK(message->handles);
-  DCHECK(message->num_handles);
-  std::copy(message->handles, message->handles + message->num_handles, handles);
-}
-
-void SerializeRawMessagePayload(uintptr_t context, void* buffer) {
-  auto* message = reinterpret_cast<RawMessage*>(context);
-  DCHECK(message->bytes);
-  DCHECK(message->num_bytes);
-  std::copy(message->bytes, message->bytes + message->num_bytes,
-            static_cast<uint8_t*>(buffer));
-}
-
-void DoNothing(uintptr_t context) {}
-
-const MojoMessageOperationThunks kRawMessageThunks = {
-    sizeof(kRawMessageThunks),
-    &GetRawMessageSize,
-    &SerializeRawMessageHandles,
-    &SerializeRawMessagePayload,
-    &DoNothing,
-};
-
-}  // namespace
-
 MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
                            const void* bytes,
                            size_t num_bytes,
                            const MojoHandle* handles,
                            size_t num_handles,
                            MojoWriteMessageFlags flags) {
-  RawMessage message(bytes, num_bytes, handles, num_handles);
   ScopedMessageHandle message_handle;
-  MojoResult rv = CreateMessage(reinterpret_cast<uintptr_t>(&message),
-                                &kRawMessageThunks, &message_handle);
+  MojoResult rv = CreateMessage(&message_handle);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
 
-  // Force the message object to be serialized immediately so we can copy the
-  // local data in.
-  if (MojoSerializeMessage(message_handle->value()) != MOJO_RESULT_OK) {
-    // If serialization fails for some reason (e.g. invalid handles) we must
-    // be careful to not propagate the message object further. It is unsafe for
-    // the message's unserialized context to persist beyond the scope of this
-    // function.
+  void* buffer;
+  uint32_t buffer_size;
+  rv = MojoAttachSerializedMessageBuffer(
+      message_handle->value(), base::checked_cast<uint32_t>(num_bytes), handles,
+      base::checked_cast<uint32_t>(num_handles), &buffer, &buffer_size);
+  if (rv != MOJO_RESULT_OK)
     return MOJO_RESULT_ABORTED;
-  }
+
+  DCHECK(buffer);
+  DCHECK_GE(buffer_size, base::checked_cast<uint32_t>(num_bytes));
+  memcpy(buffer, bytes, num_bytes);
 
   return MojoWriteMessage(message_pipe.value(),
                           message_handle.release().value(), flags);
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 42b107d..cd8f0f8 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -43,6 +43,7 @@
   "//ui/events/mojo/typemaps.gni",
   "//ui/gfx/typemaps.gni",
   "//ui/latency/mojo/typemaps.gni",
+  "//ui/ozone/public/interfaces/typemaps.gni",
   "//ui/message_center/mojo/typemaps.gni",
   "//url/mojo/typemaps.gni",
 ]
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index c196e68..a61f168d 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -612,8 +612,7 @@
   entry_ptr->disk_entry->Doom();
   entry_ptr->doomed = true;
 
-  DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
-         entry_ptr->headers_transaction ||
+  DCHECK(!entry_ptr->HasNoTransactions() ||
          entry_ptr->will_process_queued_transactions);
   return OK;
 }
@@ -915,6 +914,10 @@
       // Restart already validated transactions so that they are able to read
       // the truncated status of the entry.
       RestartHeadersPhaseTransactions(entry, transaction);
+      if (entry->HasNoTransactions() &&
+          !entry->will_process_queued_transactions) {
+        DestroyEntry(entry);
+      }
       return;
     }
     DoneWritingToEntry(entry, success && !did_truncate, transaction);
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index e37c7b0..e9248ba 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -928,6 +928,9 @@
     case kTLS13VariantExperiment:
       SSL_set_tls13_variant(ssl_.get(), tls13_experiment);
       break;
+    case kTLS13VariantRecordTypeExperiment:
+      SSL_set_tls13_variant(ssl_.get(), tls13_record_type_experiment);
+      break;
   }
 
   // OpenSSL defaults some options to on, others to off. To avoid ambiguity,
diff --git a/net/ssl/ssl_config.h b/net/ssl/ssl_config.h
index 4a2a13b..c0b2357 100644
--- a/net/ssl/ssl_config.h
+++ b/net/ssl/ssl_config.h
@@ -38,6 +38,7 @@
 enum TLS13Variant {
   kTLS13VariantDraft = 0,
   kTLS13VariantExperiment = 1,
+  kTLS13VariantRecordTypeExperiment = 2,
 };
 
 // Default minimum protocol version.
diff --git a/remoting/ios/BUILD.gn b/remoting/ios/BUILD.gn
index 67589d8..d3108ae 100644
--- a/remoting/ios/BUILD.gn
+++ b/remoting/ios/BUILD.gn
@@ -27,28 +27,16 @@
     "client_gestures.mm",
     "client_keyboard.h",
     "client_keyboard.mm",
-    "host_preferences.h",
-    "host_preferences.mm",
-    "host_preferences_persistence.h",
     "keychain_wrapper.h",
     "keychain_wrapper.mm",
   ]
 
-  if (!is_chrome_branded) {
-    sources += [ "host_preferences_persistence_chromium.mm" ]
-  } else {
-    # TODO(nicholss): Until we have a private implementation of this, stub it
-    # with the chromium version. We still want the logic of is_chrome_branded
-    # but to get the release builds building we will just reuse the file.
-    sources += [ "host_preferences_persistence_chromium.mm" ]
-  }
-
   public_deps = [
     "//remoting/ios/domain",
     "//remoting/ios/facade",
     "//remoting/ios/session",
-    "//third_party/webrtc/rtc_base:rtc_base",
     "//third_party/webrtc/modules/desktop_capture:primitives",
+    "//third_party/webrtc/rtc_base:rtc_base",
   ]
 
   deps = [
diff --git a/remoting/ios/app/BUILD.gn b/remoting/ios/app/BUILD.gn
index 7fc1ce8..2bee702c 100644
--- a/remoting/ios/app/BUILD.gn
+++ b/remoting/ios/app/BUILD.gn
@@ -70,7 +70,9 @@
     "//remoting/ios/app/resources:assets",
     "//remoting/ios/app/settings",
     "//remoting/ios/display",
+    "//remoting/ios/domain",
     "//remoting/ios/mdc",
+    "//remoting/ios/persistence",
     "//remoting/protocol",
     "//ui/base",
     "//ui/gfx",
diff --git a/remoting/ios/app/host_view_controller.mm b/remoting/ios/app/host_view_controller.mm
index ea36e730..4af3cd89 100644
--- a/remoting/ios/app/host_view_controller.mm
+++ b/remoting/ios/app/host_view_controller.mm
@@ -16,7 +16,10 @@
 #import "remoting/ios/client_gestures.h"
 #import "remoting/ios/client_keyboard.h"
 #import "remoting/ios/display/eagl_view.h"
+#import "remoting/ios/domain/host_info.h"
+#import "remoting/ios/domain/host_settings.h"
 #import "remoting/ios/mdc/MDCActionImageView.h"
+#import "remoting/ios/persistence/remoting_preferences.h"
 #import "remoting/ios/session/remoting_client.h"
 
 #include "base/strings/sys_string_conversions.h"
@@ -37,6 +40,7 @@
   ClientKeyboard* _clientKeyboard;
   CGSize _keyboardSize;
   BOOL _surfaceCreated;
+  HostSettings* _settings;
 }
 @end
 
@@ -48,6 +52,8 @@
     _client = client;
     _keyboardSize = CGSizeZero;
     _surfaceCreated = NO;
+    _settings =
+        [[RemotingPreferences instance] settingsForHost:client.hostInfo.hostId];
   }
   return self;
 }
@@ -81,9 +87,7 @@
   [_floatingButton addSubview:_actionImageView];
   [self.view addSubview:_floatingButton];
 
-  // TODO(yuweih): This should be loaded from and stored into user defaults.
-  _client.gestureInterpreter->SetInputMode(
-      remoting::GestureInterpreter::DIRECT_INPUT_MODE);
+  [self applyInputMode];
 }
 
 - (void)viewDidUnload {
@@ -135,6 +139,9 @@
 - (void)viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
 
+  [[RemotingPreferences instance] setSettings:_settings
+                                      forHost:_client.hostInfo.hostId];
+
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
@@ -242,15 +249,13 @@
 }
 
 - (void)useDirectInputMode {
-  // TODO(nicholss): Store this as a preference.
-  _client.gestureInterpreter->SetInputMode(
-      remoting::GestureInterpreter::DIRECT_INPUT_MODE);
+  _settings.inputMode = ClientInputModeDirect;
+  [self applyInputMode];
 }
 
 - (void)useTrackpadInputMode {
-  // TODO(nicholss): Store this as a preference.
-  _client.gestureInterpreter->SetInputMode(
-      remoting::GestureInterpreter::TRACKPAD_INPUT_MODE);
+  _settings.inputMode = ClientInputModeTrackpad;
+  [self applyInputMode];
 }
 
 - (void)sendCtrAltDel {
@@ -263,6 +268,19 @@
 
 #pragma mark - Private
 
+- (void)applyInputMode {
+  switch (_settings.inputMode) {
+    case ClientInputModeTrackpad:
+      _client.gestureInterpreter->SetInputMode(
+          remoting::GestureInterpreter::TRACKPAD_INPUT_MODE);
+      break;
+    case ClientInputModeDirect:  // Fall-through.
+    default:
+      _client.gestureInterpreter->SetInputMode(
+          remoting::GestureInterpreter::DIRECT_INPUT_MODE);
+  }
+}
+
 - (void)didTap:(id)sender {
   // TODO(nicholss): The FAB is being used to launch an alert window with
   // more options. This is not ideal but it gets us an easy way to make a
@@ -298,10 +316,15 @@
           ? @"Trackpad Mode"
           : @"Touch Mode";
   void (^switchInputModeHandler)(UIAlertAction*) = ^(UIAlertAction*) {
-    _client.gestureInterpreter->SetInputMode(
-        currentInputMode == remoting::GestureInterpreter::DIRECT_INPUT_MODE
-            ? remoting::GestureInterpreter::TRACKPAD_INPUT_MODE
-            : remoting::GestureInterpreter::DIRECT_INPUT_MODE);
+    switch (currentInputMode) {
+      case remoting::GestureInterpreter::DIRECT_INPUT_MODE:
+        [self useTrackpadInputMode];
+        break;
+      case remoting::GestureInterpreter::TRACKPAD_INPUT_MODE:  // Fall-through.
+      default:
+        [self useDirectInputMode];
+        break;
+    }
     [_actionImageView setActive:NO animated:YES];
   };
   [alert addAction:[UIAlertAction actionWithTitle:switchInputModeTitle
diff --git a/remoting/ios/domain/BUILD.gn b/remoting/ios/domain/BUILD.gn
index 4f708511..238f6fc 100644
--- a/remoting/ios/domain/BUILD.gn
+++ b/remoting/ios/domain/BUILD.gn
@@ -12,6 +12,8 @@
     "client_session_details.mm",
     "host_info.h",
     "host_info.mm",
+    "host_settings.h",
+    "host_settings.mm",
     "user_info.h",
     "user_info.mm",
   ]
diff --git a/remoting/ios/domain/host_settings.h b/remoting/ios/domain/host_settings.h
new file mode 100644
index 0000000..25e95b6
--- /dev/null
+++ b/remoting/ios/domain/host_settings.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_IOS_DOMAIN_HOST_SETTINGS_H_
+#define REMOTING_IOS_DOMAIN_HOST_SETTINGS_H_
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger, ClientInputMode) {
+  ClientInputModeUndefined,
+  ClientInputModeDirect,
+  ClientInputModeTrackpad,
+};
+
+// A detail record for a Remoting Settings.
+@interface HostSettings : NSObject<NSCoding>
+
+// Various properties of the Remoting Settings.
+@property(nonatomic, copy) NSString* hostId;
+@property(nonatomic) ClientInputMode inputMode;
+
+@end
+
+#endif  //  REMOTING_IOS_DOMAIN_HOST_SETTINGS_H_
diff --git a/remoting/ios/domain/host_settings.mm b/remoting/ios/domain/host_settings.mm
new file mode 100644
index 0000000..4c8e9503
--- /dev/null
+++ b/remoting/ios/domain/host_settings.mm
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "remoting/ios/domain/host_settings.h"
+
+@implementation HostSettings
+
+@synthesize hostId = _hostId;
+@synthesize inputMode = _inputMode;
+
+- (id)initWithCoder:(NSCoder*)coder {
+  self = [super init];
+  if (self) {
+    self.hostId = [coder decodeObjectForKey:@"hostId"];
+    NSNumber* mode = [coder decodeObjectForKey:@"inputMode"];
+    self.inputMode = (ClientInputMode)[mode intValue];
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)coder {
+  [coder encodeObject:self.hostId forKey:@"hostId"];
+  NSNumber* mode = [NSNumber numberWithInt:self.inputMode];
+  [coder encodeObject:mode forKey:@"inputMode"];
+}
+
+- (NSString*)description {
+  return [NSString stringWithFormat:@"HostSettings: hostId=%@ inputMode=%d",
+                                    _hostId, (int)_inputMode];
+}
+
+@end
diff --git a/remoting/ios/facade/BUILD.gn b/remoting/ios/facade/BUILD.gn
index 681ce32..c2f6623 100644
--- a/remoting/ios/facade/BUILD.gn
+++ b/remoting/ios/facade/BUILD.gn
@@ -25,6 +25,7 @@
     "//base",
     "//remoting/base:authorization",
     "//remoting/ios/domain",
+    "//remoting/ios/persistence",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/remoting/ios/facade/remoting_oauth_authentication.mm b/remoting/ios/facade/remoting_oauth_authentication.mm
index 5bbbbf04..39cbc52 100644
--- a/remoting/ios/facade/remoting_oauth_authentication.mm
+++ b/remoting/ios/facade/remoting_oauth_authentication.mm
@@ -18,6 +18,7 @@
 #import "remoting/ios/facade/ios_client_runtime_delegate.h"
 #import "remoting/ios/facade/remoting_service.h"
 #import "remoting/ios/keychain_wrapper.h"
+#import "remoting/ios/persistence/remoting_preferences.h"
 
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
@@ -25,9 +26,6 @@
 #include "remoting/base/oauth_token_getter.h"
 #include "remoting/base/oauth_token_getter_impl.h"
 
-static NSString* const kCRDAuthenticatedUserEmailKey =
-    @"kCRDAuthenticatedUserEmailKey";
-
 const char kOauthRedirectUrl[] =
     "https://chromoting-oauth.talkgadget."
     "google.com/talkgadget/oauth/chrome-remote-desktop/dev";
@@ -184,22 +182,19 @@
 #pragma mark - Persistence
 
 - (void)storeUserInfo:(UserInfo*)user {
-  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
   if (user) {
-    [defaults setObject:user.userEmail forKey:kCRDAuthenticatedUserEmailKey];
+    [RemotingPreferences instance].activeUserKey = user.userEmail;
     // TODO(nicholss): Need to match the token with the email.
     [_keychainWrapper setRefreshToken:user.refreshToken];
   } else {
-    [defaults removeObjectForKey:kCRDAuthenticatedUserEmailKey];
+    [RemotingPreferences instance].activeUserKey = nil;
     [_keychainWrapper resetKeychainItem];
   }
-  [defaults synchronize];
 }
 
 - (UserInfo*)loadUserInfo {
   UserInfo* user = [[UserInfo alloc] init];
-  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  user.userEmail = [defaults objectForKey:kCRDAuthenticatedUserEmailKey];
+  user.userEmail = [RemotingPreferences instance].activeUserKey;
   // TODO(nicholss): Need to match the token with the email.
   user.refreshToken = [_keychainWrapper refreshToken];
 
diff --git a/remoting/ios/facade/remoting_service.h b/remoting/ios/facade/remoting_service.h
index af15a60..0d315d2 100644
--- a/remoting/ios/facade/remoting_service.h
+++ b/remoting/ios/facade/remoting_service.h
@@ -38,13 +38,13 @@
 // singleton and should only be accessed via the |SharedInstance| method.
 @interface RemotingService : NSObject
 
-// Access to the singleton shared instance from this method.
-+ (RemotingService*)instance;
-
 // Start a request to fetch the host list. This will produce an notification on
 // |kHostsDidUpdate| when a new host is ready.
 - (void)requestHostListFetch;
 
+// Access to the singleton shared instance from this property.
+@property(nonatomic, readonly, class) RemotingService* instance;
+
 // Returns the current host list.
 @property(nonatomic, readonly) NSArray<HostInfo*>* hosts;
 
diff --git a/remoting/ios/host_preferences.h b/remoting/ios/host_preferences.h
deleted file mode 100644
index d8dba18..0000000
--- a/remoting/ios/host_preferences.h
+++ /dev/null
@@ -1,28 +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 REMOTING_IOS_HOST_PREFERENCES_H_
-#define REMOTING_IOS_HOST_PREFERENCES_H_
-
-#import <CoreData/CoreData.h>
-
-// A HostPreferences contains details to negotiate and maintain a connection
-// to a remote Chromoting host.  This is an entity in a backing store.
-@interface HostPreferences : NSObject<NSCoding>
-
-// Properties supplied by the host server.
-@property(nonatomic, copy) NSString* hostId;
-@property(nonatomic, copy) NSString* pairId;
-@property(nonatomic, copy) NSString* pairSecret;
-
-// Commit this record using the Keychain for current identity.
-- (void)saveToKeychain;
-
-// Load a record from the Keychain for current identity.
-// If a record does not exist, return a new record with a blank secret.
-+ (HostPreferences*)hostForId:(NSString*)hostId;
-
-@end
-
-#endif  // REMOTING_IOS_HOST_PREFERENCES_H_
diff --git a/remoting/ios/host_preferences.mm b/remoting/ios/host_preferences.mm
deleted file mode 100644
index d0c0efc..0000000
--- a/remoting/ios/host_preferences.mm
+++ /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.
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#import "remoting/ios/host_preferences.h"
-
-#import "base/logging.h"
-#import "remoting/ios/host_preferences_persistence.h"
-
-namespace {
-static NSString* const kHostPreferencesDataKeyHostDictionary =
-    @"kHostPreferencesDataKeyHostDictionary";
-static NSString* const kHostPreferencesHostIdKey = @"HostId";
-static NSString* const kHostPreferencesPairIdKey = @"PairId";
-static NSString* const kHostPreferencesPairSecretKey = @"PairSecret";
-}  // namespace
-
-@interface HostPreferences ()
-
-// Load the known hosts from the Keychain.
-// If no data exists, return an empty dictionary
-+ (NSMutableDictionary*)loadHosts;
-
-@end
-
-@implementation HostPreferences
-
-@synthesize hostId = _hostId;
-@synthesize pairId = _pairId;
-@synthesize pairSecret = _pairSecret;
-
-#pragma mark - Public
-
-- (void)saveToKeychain {
-  NSMutableDictionary* hosts = [HostPreferences loadHosts];
-  [hosts setObject:self forKey:_hostId];
-
-  NSData* writeData = [NSKeyedArchiver archivedDataWithRootObject:hosts];
-
-  NSError* keychainError =
-      remoting::ios::WriteHostPreferencesToKeychain(writeData);
-
-  LOG_IF(ERROR, !keychainError) << "Could not write to keychain.";
-}
-
-+ (HostPreferences*)hostForId:(NSString*)hostId {
-  NSMutableDictionary* hosts = [HostPreferences loadHosts];
-  HostPreferences* host = hosts[hostId];
-  if (!host) {
-    host = [[HostPreferences alloc] init];
-    host.hostId = hostId;
-    host.pairId = @"";
-    host.pairSecret = @"";
-  }
-  return host;
-}
-
-#pragma mark - Private
-
-+ (NSMutableDictionary*)loadHosts {
-  NSData* readData = remoting::ios::ReadHostPreferencesFromKeychain();
-  if (readData) {
-    return [NSKeyedUnarchiver unarchiveObjectWithData:readData];
-  } else {
-    return [[NSMutableDictionary alloc] init];
-  }
-}
-
-#pragma mark - NSCoding
-
-- (instancetype)initWithCoder:(NSCoder*)coder {
-  self = [super init];
-  if (self) {
-    [self setHostId:[coder decodeObjectForKey:kHostPreferencesHostIdKey]];
-    [self setPairId:[coder decodeObjectForKey:kHostPreferencesPairIdKey]];
-    [self
-        setPairSecret:[coder decodeObjectForKey:kHostPreferencesPairSecretKey]];
-  }
-  return self;
-}
-
-- (void)encodeWithCoder:(NSCoder*)coder {
-  [coder encodeObject:_hostId forKey:kHostPreferencesHostIdKey];
-  [coder encodeObject:_pairId forKey:kHostPreferencesPairIdKey];
-  [coder encodeObject:_pairSecret forKey:kHostPreferencesPairSecretKey];
-}
-
-@end
diff --git a/remoting/ios/host_preferences_persistence.h b/remoting/ios/host_preferences_persistence.h
deleted file mode 100644
index 486b64f..0000000
--- a/remoting/ios/host_preferences_persistence.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_IOS_HOST_PREFERENCES_PERSISTENCE_H_
-#define REMOTING_IOS_HOST_PREFERENCES_PERSISTENCE_H_
-
-#import <CoreData/CoreData.h>
-
-// Methods used to store and recall Host Prefrences on the keychain.
-// Used to cache data for quicker connection to previously fetched host data.
-namespace remoting {
-namespace ios {
-
-NSError* WriteHostPreferencesToKeychain(NSData* data);
-NSData* ReadHostPreferencesFromKeychain();
-
-}  // namespace ios
-}  // namespace remoting
-
-#endif  // REMOTING_IOS_HOST_PREFERENCES_PERSISTENCE_H_
diff --git a/remoting/ios/host_preferences_persistence_chromium.mm b/remoting/ios/host_preferences_persistence_chromium.mm
deleted file mode 100644
index a2258db4..0000000
--- a/remoting/ios/host_preferences_persistence_chromium.mm
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "remoting/ios/host_preferences_persistence.h"
-
-#import "base/logging.h"
-
-namespace remoting {
-namespace ios {
-
-// TODO(nicholss): It might be useful to save |data| in a static variable,
-// which is then returned from ReadHostPreferencesFromKeychain(). This would
-// allow to test pairing, even though the pairing info is not persisted when
-// the app is restarted.
-
-NSError* WriteHostPreferencesToKeychain(NSData* data) {
-  NOTIMPLEMENTED();
-  return nil;
-}
-
-NSData* ReadHostPreferencesFromKeychain() {
-  NOTIMPLEMENTED();
-  return nil;
-}
-
-}  // namespace ios
-}  // namespace remoting
diff --git a/remoting/ios/persistence/BUILD.gn b/remoting/ios/persistence/BUILD.gn
new file mode 100644
index 0000000..54afc44a
--- /dev/null
+++ b/remoting/ios/persistence/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.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/ios/rules.gni")
+import("//remoting/build/config/remoting_build.gni")
+
+source_set("persistence") {
+  sources = [
+    "remoting_preferences.h",
+    "remoting_preferences.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//remoting/ios/domain",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/remoting/ios/persistence/remoting_preferences.h b/remoting/ios/persistence/remoting_preferences.h
new file mode 100644
index 0000000..cd677196
--- /dev/null
+++ b/remoting/ios/persistence/remoting_preferences.h
@@ -0,0 +1,26 @@
+// 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 REMOTING_IOS_PERSISTENCE_REMOTING_PREFERENCES_H_
+#define REMOTING_IOS_PERSISTENCE_REMOTING_PREFERENCES_H_
+
+#import <Foundation/Foundation.h>
+
+@class HostSettings;
+
+// |RemotingPreferences| is the centralized place to ask for information about
+// defaults and prefrences.
+@interface RemotingPreferences : NSObject
+
+- (HostSettings*)settingsForHost:(NSString*)hostId;
+- (void)setSettings:(HostSettings*)settings forHost:(NSString*)hostId;
+
+// Access to the singleton shared instance from this property.
+@property(nonatomic, readonly, class) RemotingPreferences* instance;
+
+@property(nonatomic) NSString* activeUserKey;
+
+@end
+
+#endif  // REMOTING_IOS_PERSISTENCE_REMOTING_PREFERENCES_H_
diff --git a/remoting/ios/persistence/remoting_preferences.mm b/remoting/ios/persistence/remoting_preferences.mm
new file mode 100644
index 0000000..a148c9b
--- /dev/null
+++ b/remoting/ios/persistence/remoting_preferences.mm
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "remoting/ios/persistence/remoting_preferences.h"
+
+#import "base/mac/bind_objc_block.h"
+#import "remoting/ios/domain/host_info.h"
+#import "remoting/ios/domain/host_settings.h"
+
+#include "base/logging.h"
+
+static NSString* const kActiveUserKey = @"kActiveUserKey";
+static NSString* const kHostSettingsKey = @"kHostSettingsKey";
+
+@interface RemotingPreferences () {
+  NSUserDefaults* _defaults;
+}
+@end
+
+@implementation RemotingPreferences
+
+// RemotingService is a singleton.
++ (RemotingPreferences*)instance {
+  static RemotingPreferences* sharedInstance = nil;
+  static dispatch_once_t guard;
+  dispatch_once(&guard, ^{
+    sharedInstance = [[RemotingPreferences alloc] init];
+  });
+  return sharedInstance;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _defaults = [NSUserDefaults standardUserDefaults];
+  }
+  return self;
+}
+
+#pragma mark - RemotingPreferences Implementation
+
+- (HostSettings*)settingsForHost:(NSString*)hostId {
+  NSString* key =
+      [NSString stringWithFormat:@"%@-%@", kHostSettingsKey, hostId];
+  NSData* encodedSettings = [_defaults objectForKey:key];
+  HostSettings* settings =
+      [NSKeyedUnarchiver unarchiveObjectWithData:encodedSettings];
+  if (settings == nil) {
+    settings = [[HostSettings alloc] init];
+    settings.hostId = hostId;
+    [self setSettings:settings forHost:hostId];
+  }
+  return settings;
+}
+
+- (void)setSettings:(HostSettings*)settings forHost:(NSString*)hostId {
+  NSString* key =
+      [NSString stringWithFormat:@"%@-%@", kHostSettingsKey, hostId];
+  if (settings) {
+    NSData* encodedSettings =
+        [NSKeyedArchiver archivedDataWithRootObject:settings];
+    [_defaults setObject:encodedSettings forKey:key];
+  } else {
+    return [_defaults removeObjectForKey:key];
+  }
+  [_defaults synchronize];
+}
+
+#pragma mark - Properties
+
+- (void)setActiveUserKey:(NSString*)activeUserKey {
+  if (activeUserKey) {
+    [_defaults setObject:activeUserKey forKey:kActiveUserKey];
+  } else {
+    [_defaults removeObjectForKey:kActiveUserKey];
+  }
+  [_defaults synchronize];
+}
+
+- (NSString*)activeUserKey {
+  return [_defaults objectForKey:kActiveUserKey];
+}
+
+@end
diff --git a/services/shape_detection/barcode_detection_impl_mac.h b/services/shape_detection/barcode_detection_impl_mac.h
index 062a68b..51f32b3 100644
--- a/services/shape_detection/barcode_detection_impl_mac.h
+++ b/services/shape_detection/barcode_detection_impl_mac.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_H_
 #define SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_H_
 
+#include "base/mac/availability.h"
 #include "base/mac/scoped_nsobject.h"
 #include "services/shape_detection/public/interfaces/barcodedetection.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -13,7 +14,10 @@
 
 namespace shape_detection {
 
-class BarcodeDetectionImplMac
+// The __attribute__ visibility annotation is necessary to work around a clang
+// bug: https://bugs.llvm.org/show_bug.cgi?id=33796.
+class API_AVAILABLE(macosx(10.10))
+    __attribute__((visibility("hidden"))) BarcodeDetectionImplMac
     : public shape_detection::mojom::BarcodeDetection {
  public:
   BarcodeDetectionImplMac();
diff --git a/services/shape_detection/barcode_detection_impl_mac.mm b/services/shape_detection/barcode_detection_impl_mac.mm
index 6aa128f..d449d8c 100644
--- a/services/shape_detection/barcode_detection_impl_mac.mm
+++ b/services/shape_detection/barcode_detection_impl_mac.mm
@@ -29,19 +29,17 @@
     const service_manager::BindSourceInfo& source_info,
     shape_detection::mojom::BarcodeDetectionRequest request) {
   // Barcode detection needs at least MAC OS X 10.10.
-  if (!base::mac::IsAtLeastOS10_10())
-    return;
-  mojo::MakeStrongBinding(base::MakeUnique<BarcodeDetectionImplMac>(),
-                          std::move(request));
+  if (@available(macOS 10.10, *)) {
+    mojo::MakeStrongBinding(base::MakeUnique<BarcodeDetectionImplMac>(),
+                            std::move(request));
+  }
 }
 
 BarcodeDetectionImplMac::BarcodeDetectionImplMac() {
   NSDictionary* const options = @{CIDetectorAccuracy : CIDetectorAccuracyHigh};
-  if (@available(macOS 10.10, *)) {
-    detector_.reset([[CIDetector detectorOfType:CIDetectorTypeQRCode
-                                        context:nil
-                                        options:options] retain]);
-  }
+  detector_.reset([[CIDetector detectorOfType:CIDetectorTypeQRCode
+                                      context:nil
+                                      options:options] retain]);
 }
 
 BarcodeDetectionImplMac::~BarcodeDetectionImplMac() {}
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
index b30879c..386dbe9 100644
--- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -31,7 +31,7 @@
   }
   MOCK_METHOD2(Detection, void(size_t, const std::string&));
 
-  BarcodeDetectionImplMac impl_;
+  API_AVAILABLE(macosx(10.10)) std::unique_ptr<BarcodeDetectionImplMac> impl_;
   const base::MessageLoop message_loop_;
 };
 
@@ -41,51 +41,54 @@
 TEST_F(BarcodeDetectionImplMacTest, ScanOneBarcode) {
   // Barcode detection needs at least MAC OS X 10.10, and GPU infrastructure.
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseGpuInTests) ||
-      !base::mac::IsAtLeastOS10_10()) {
+          switches::kUseGpuInTests)) {
     return;
   }
 
-  const std::string kInfoString = "https://www.chromium.org";
-  // Generate a QR code image as a CIImage by using |qr_code_generator|.
-  NSData* const qr_code_data =
-      [[NSString stringWithUTF8String:kInfoString.c_str()]
-          dataUsingEncoding:NSISOLatin1StringEncoding];
-  // TODO(mcasas): Consider using other generator types (e.g.
-  // CI{AztecCode,Code128Barcode,PDF417Barcode}Generator) when the minimal OS X
-  // is upgraded to 10.10+ (https://crbug.com/624049).
-  CIFilter* qr_code_generator =
-      [CIFilter filterWithName:@"CIQRCodeGenerator"];
-  [qr_code_generator setValue:qr_code_data forKey:@"inputMessage"];
+  if (@available(macOS 10.10, *)) {
+    impl_.reset(new BarcodeDetectionImplMac);
+    const std::string kInfoString = "https://www.chromium.org";
+    // Generate a QR code image as a CIImage by using |qr_code_generator|.
+    NSData* const qr_code_data =
+        [[NSString stringWithUTF8String:kInfoString.c_str()]
+            dataUsingEncoding:NSISOLatin1StringEncoding];
+    // TODO(mcasas): Consider using other generator types (e.g.
+    // CI{AztecCode,Code128Barcode,PDF417Barcode}Generator) when the minimal OS
+    // X is upgraded to 10.10+ (https://crbug.com/624049).
+    CIFilter* qr_code_generator =
+        [CIFilter filterWithName:@"CIQRCodeGenerator"];
+    [qr_code_generator setValue:qr_code_data forKey:@"inputMessage"];
 
-  // [CIImage outputImage] is available in macOS 10.10+.  Could be added to
-  // sdk_forward_declarations.h but seems hardly worth it.
-  EXPECT_TRUE([qr_code_generator respondsToSelector:@selector(outputImage)]);
-  CIImage* qr_code_image =
-      [qr_code_generator performSelector:@selector(outputImage)];
+    // [CIImage outputImage] is available in macOS 10.10+.  Could be added to
+    // sdk_forward_declarations.h but seems hardly worth it.
+    EXPECT_TRUE([qr_code_generator respondsToSelector:@selector(outputImage)]);
+    CIImage* qr_code_image =
+        [qr_code_generator performSelector:@selector(outputImage)];
 
-  const gfx::Size size([qr_code_image extent].size.width,
-                       [qr_code_image extent].size.height);
+    const gfx::Size size([qr_code_image extent].size.width,
+                         [qr_code_image extent].size.height);
 
-  base::scoped_nsobject<CIContext> context([[CIContext alloc] init]);
+    base::scoped_nsobject<CIContext> context([[CIContext alloc] init]);
 
-  base::ScopedCFTypeRef<CGImageRef> cg_image(
-      [context createCGImage:qr_code_image fromRect:[qr_code_image extent]]);
-  EXPECT_EQ(static_cast<size_t>(size.width()), CGImageGetWidth(cg_image));
-  EXPECT_EQ(static_cast<size_t>(size.height()), CGImageGetHeight(cg_image));
+    base::ScopedCFTypeRef<CGImageRef> cg_image(
+        [context createCGImage:qr_code_image fromRect:[qr_code_image extent]]);
+    EXPECT_EQ(static_cast<size_t>(size.width()), CGImageGetWidth(cg_image));
+    EXPECT_EQ(static_cast<size_t>(size.height()), CGImageGetHeight(cg_image));
 
-  SkBitmap bitmap;
-  ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
+    SkBitmap bitmap;
+    ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
 
-  base::RunLoop run_loop;
-  base::Closure quit_closure = run_loop.QuitClosure();
-  // Send the image Detect() and expect the response in callback.
-  EXPECT_CALL(*this, Detection(1, kInfoString))
-      .WillOnce(RunClosure(quit_closure));
-  impl_.Detect(bitmap, base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
-                                  base::Unretained(this)));
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    // Send the image Detect() and expect the response in callback.
+    EXPECT_CALL(*this, Detection(1, kInfoString))
+        .WillOnce(RunClosure(quit_closure));
+    impl_->Detect(bitmap,
+                  base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
+                             base::Unretained(this)));
 
-  run_loop.Run();
+    run_loop.Run();
+  }
 }
 
 }  // shape_detection namespace
diff --git a/services/shape_detection/text_detection_impl_mac.h b/services/shape_detection/text_detection_impl_mac.h
index 64563c67..83a5f177 100644
--- a/services/shape_detection/text_detection_impl_mac.h
+++ b/services/shape_detection/text_detection_impl_mac.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_SHAPE_DETECTION_TEXT_DETECTION_IMPL_MAC_H_
 #define SERVICES_SHAPE_DETECTION_TEXT_DETECTION_IMPL_MAC_H_
 
+#include "base/mac/availability.h"
 #include "base/mac/scoped_nsobject.h"
 #include "services/shape_detection/public/interfaces/textdetection.mojom.h"
 
@@ -12,7 +13,11 @@
 
 namespace shape_detection {
 
-class TextDetectionImplMac : public mojom::TextDetection {
+// The __attribute__ visibility annotation is necessary to work around a clang
+// bug: https://bugs.llvm.org/show_bug.cgi?id=33796.
+class API_AVAILABLE(macosx(10.11))
+    __attribute__((visibility("hidden"))) TextDetectionImplMac
+    : public mojom::TextDetection {
  public:
   TextDetectionImplMac();
   ~TextDetectionImplMac() override;
diff --git a/services/shape_detection/text_detection_impl_mac.mm b/services/shape_detection/text_detection_impl_mac.mm
index f645cce..a2fec7a 100644
--- a/services/shape_detection/text_detection_impl_mac.mm
+++ b/services/shape_detection/text_detection_impl_mac.mm
@@ -28,17 +28,17 @@
     const service_manager::BindSourceInfo& source_info,
     mojom::TextDetectionRequest request) {
   // Text detection needs at least MAC OS X 10.11.
-  if (!base::mac::IsAtLeastOS10_11())
-    return;
-  mojo::MakeStrongBinding(base::MakeUnique<TextDetectionImplMac>(),
-                          std::move(request));
+  if (@available(macOS 10.11, *)) {
+    mojo::MakeStrongBinding(base::MakeUnique<TextDetectionImplMac>(),
+                            std::move(request));
+  }
 }
 
 TextDetectionImplMac::TextDetectionImplMac() {
   NSDictionary* const opts = @{CIDetectorAccuracy : CIDetectorAccuracyHigh};
-  detector_.reset([[CIDetector detectorOfType:CIDetectorTypeText
-                                      context:nil
-                                      options:opts] retain]);
+  detector_.reset(
+      [[CIDetector detectorOfType:CIDetectorTypeText context:nil options:opts]
+          retain]);
 }
 
 TextDetectionImplMac::~TextDetectionImplMac() {}
diff --git a/services/shape_detection/text_detection_impl_mac_unittest.mm b/services/shape_detection/text_detection_impl_mac_unittest.mm
index 71def0f..8981645 100644
--- a/services/shape_detection/text_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/text_detection_impl_mac_unittest.mm
@@ -32,7 +32,7 @@
   }
   MOCK_METHOD1(Detection, void(size_t));
 
-  TextDetectionImplMac impl_;
+  API_AVAILABLE(macosx(10.11)) std::unique_ptr<TextDetectionImplMac> impl_;
   const base::MessageLoop message_loop_;
 };
 
@@ -47,53 +47,56 @@
     return;
   }
 
-  base::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace(
-      CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+  if (@available(macOS 10.11, *)) {
+    impl_.reset(new TextDetectionImplMac);
+    base::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace(
+        CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
 
-  const int width = 200;
-  const int height = 50;
-  base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
-      nullptr, width, height, 8 /* bitsPerComponent */,
-      width * 4 /* rowBytes */, rgb_colorspace,
-      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+    const int width = 200;
+    const int height = 50;
+    base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
+        nullptr, width, height, 8 /* bitsPerComponent */,
+        width * 4 /* rowBytes */, rgb_colorspace,
+        kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
 
-  // Draw a white background.
-  CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
-  CGContextFillRect(context, CGRectMake(0.0, 0.0, width, height));
+    // Draw a white background.
+    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
+    CGContextFillRect(context, CGRectMake(0.0, 0.0, width, height));
 
-  // Create a line of Helvetica 16 text, and draw it in the |context|.
-  base::scoped_nsobject<NSFont> helvetica(
-      [NSFont fontWithName:@"Helvetica" size:16]);
-  NSDictionary* attributes = [NSDictionary
-      dictionaryWithObjectsAndKeys:helvetica, kCTFontAttributeName, nil];
+    // Create a line of Helvetica 16 text, and draw it in the |context|.
+    base::scoped_nsobject<NSFont> helvetica(
+        [NSFont fontWithName:@"Helvetica" size:16]);
+    NSDictionary* attributes = [NSDictionary
+        dictionaryWithObjectsAndKeys:helvetica, kCTFontAttributeName, nil];
 
-  base::scoped_nsobject<NSAttributedString> info([[NSAttributedString alloc]
-      initWithString:@"https://www.chromium.org"
-          attributes:attributes]);
+    base::scoped_nsobject<NSAttributedString> info([[NSAttributedString alloc]
+        initWithString:@"https://www.chromium.org"
+            attributes:attributes]);
 
-  base::ScopedCFTypeRef<CTLineRef> line(
-      CTLineCreateWithAttributedString((CFAttributedStringRef)info.get()));
+    base::ScopedCFTypeRef<CTLineRef> line(
+        CTLineCreateWithAttributedString((CFAttributedStringRef)info.get()));
 
-  CGContextSetTextPosition(context, 10.0, height / 2.0);
-  CTLineDraw(line, context);
+    CGContextSetTextPosition(context, 10.0, height / 2.0);
+    CTLineDraw(line, context);
 
-  // Extract a CGImage and its raw pixels from |context|.
-  base::ScopedCFTypeRef<CGImageRef> cg_image(
-      CGBitmapContextCreateImage(context));
-  EXPECT_EQ(static_cast<size_t>(width), CGImageGetWidth(cg_image));
-  EXPECT_EQ(static_cast<size_t>(height), CGImageGetHeight(cg_image));
+    // Extract a CGImage and its raw pixels from |context|.
+    base::ScopedCFTypeRef<CGImageRef> cg_image(
+        CGBitmapContextCreateImage(context));
+    EXPECT_EQ(static_cast<size_t>(width), CGImageGetWidth(cg_image));
+    EXPECT_EQ(static_cast<size_t>(height), CGImageGetHeight(cg_image));
 
-  SkBitmap bitmap;
-  ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
+    SkBitmap bitmap;
+    ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
 
-  base::RunLoop run_loop;
-  base::Closure quit_closure = run_loop.QuitClosure();
-  // Send the image to Detect() and expect the response in callback.
-  EXPECT_CALL(*this, Detection(1)).WillOnce(RunClosure(quit_closure));
-  impl_.Detect(bitmap, base::Bind(&TextDetectionImplMacTest::DetectCallback,
-                                  base::Unretained(this)));
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    // Send the image to Detect() and expect the response in callback.
+    EXPECT_CALL(*this, Detection(1)).WillOnce(RunClosure(quit_closure));
+    impl_->Detect(bitmap, base::Bind(&TextDetectionImplMacTest::DetectCallback,
+                                     base::Unretained(this)));
 
-  run_loop.Run();
+    run_loop.Run();
+  }
 }
 
 }  // shape_detection namespace
diff --git a/services/ui/gpu/gpu_service.cc b/services/ui/gpu/gpu_service.cc
index 777a29a..ef4877e 100644
--- a/services/ui/gpu/gpu_service.cc
+++ b/services/ui/gpu/gpu_service.cc
@@ -11,7 +11,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "cc/output/in_process_context_provider.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/command_buffer/service/scheduler.h"
diff --git a/services/ui/public/cpp/DEPS b/services/ui/public/cpp/DEPS
index 10ed84d5..90ba774 100644
--- a/services/ui/public/cpp/DEPS
+++ b/services/ui/public/cpp/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/viz/common/gpu",
   "+gpu",
   "+mojo/gpu",
   "+skia/public/interfaces"
diff --git a/services/ui/public/cpp/gpu/context_provider_command_buffer.cc b/services/ui/public/cpp/gpu/context_provider_command_buffer.cc
index da0f48d2..81f7c19 100644
--- a/services/ui/public/cpp/gpu/context_provider_command_buffer.cc
+++ b/services/ui/public/cpp/gpu/context_provider_command_buffer.cc
@@ -17,8 +17,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/output/managed_memory_policy.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/gles2_trace_implementation.h"
@@ -317,7 +317,7 @@
     shared_providers_->list.push_back(this);
 
     cache_controller_.reset(
-        new cc::ContextCacheController(gles2_impl_.get(), task_runner));
+        new viz::ContextCacheController(gles2_impl_.get(), task_runner));
   }
   set_bind_failed.Reset();
   bind_succeeded_ = true;
@@ -392,7 +392,7 @@
   return gr_context_->get();
 }
 
-cc::ContextCacheController* ContextProviderCommandBuffer::CacheController() {
+viz::ContextCacheController* ContextProviderCommandBuffer::CacheController() {
   DCHECK(context_thread_checker_.CalledOnValidThread());
   return cache_controller_.get();
 }
diff --git a/services/ui/public/cpp/gpu/context_provider_command_buffer.h b/services/ui/public/cpp/gpu/context_provider_command_buffer.h
index a5afb59..c00c28f 100644
--- a/services/ui/public/cpp/gpu/context_provider_command_buffer.h
+++ b/services/ui/public/cpp/gpu/context_provider_command_buffer.h
@@ -15,7 +15,7 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_provider.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/scheduling_priority.h"
@@ -41,10 +41,10 @@
 
 namespace ui {
 
-// Implementation of cc::ContextProvider that provides a GL implementation over
+// Implementation of viz::ContextProvider that provides a GL implementation over
 // command buffer to the GPU process.
 class ContextProviderCommandBuffer
-    : public cc::ContextProvider,
+    : public viz::ContextProvider,
       public base::trace_event::MemoryDumpProvider {
  public:
   ContextProviderCommandBuffer(
@@ -65,13 +65,13 @@
   // on the default framebuffer.
   uint32_t GetCopyTextureInternalFormat();
 
-  // cc::ContextProvider implementation.
+  // viz::ContextProvider implementation.
   bool BindToCurrentThread() override;
   void DetachFromThread() override;
   gpu::gles2::GLES2Interface* ContextGL() override;
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
-  cc::ContextCacheController* CacheController() override;
+  viz::ContextCacheController* CacheController() override;
   void InvalidateGrContext(uint32_t state) override;
   base::Lock* GetLock() override;
   gpu::Capabilities ContextCapabilities() override;
@@ -132,7 +132,7 @@
   std::unique_ptr<gpu::gles2::GLES2Implementation> gles2_impl_;
   std::unique_ptr<gpu::gles2::GLES2TraceImplementation> trace_impl_;
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-  std::unique_ptr<cc::ContextCacheController> cache_controller_;
+  std::unique_ptr<viz::ContextCacheController> cache_controller_;
 
   LostContextCallback lost_context_callback_;
 };
diff --git a/services/ui/public/cpp/gpu/gpu.cc b/services/ui/public/cpp/gpu/gpu.cc
index 191633e..936dc5d 100644
--- a/services/ui/public/cpp/gpu/gpu.cc
+++ b/services/ui/public/cpp/gpu/gpu.cc
@@ -66,7 +66,7 @@
   return base::WrapUnique(new Gpu(std::move(factory), std::move(task_runner)));
 }
 
-scoped_refptr<cc::ContextProvider> Gpu::CreateContextProvider(
+scoped_refptr<viz::ContextProvider> Gpu::CreateContextProvider(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel) {
   int32_t stream_id = 0;
   gpu::SchedulingPriority stream_priority = gpu::SchedulingPriority::kNormal;
diff --git a/services/ui/public/cpp/gpu/gpu.h b/services/ui/public/cpp/gpu/gpu.h
index 73459fc04..c64eeae 100644
--- a/services/ui/public/cpp/gpu/gpu.h
+++ b/services/ui/public/cpp/gpu/gpu.h
@@ -13,7 +13,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 #include "services/ui/public/interfaces/gpu.mojom.h"
@@ -41,7 +41,7 @@
       const std::string& service_name,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner = nullptr);
 
-  scoped_refptr<cc::ContextProvider> CreateContextProvider(
+  scoped_refptr<viz::ContextProvider> CreateContextProvider(
       scoped_refptr<gpu::GpuChannelHost> gpu_channel);
 
   void CreateJpegDecodeAccelerator(
diff --git a/services/ui/ws/DEPS b/services/ui/ws/DEPS
index d714e80d..9e9f18d 100644
--- a/services/ui/ws/DEPS
+++ b/services/ui/ws/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/viz/common/gpu",
   "+components/viz/host",
   "+gpu/command_buffer/client",
   "+gpu/config",
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 8932277..2e34c639 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -80,7 +80,6 @@
 void EventDispatcher::SetMousePointerDisplayLocation(
     const gfx::Point& display_location,
     int64_t display_id) {
-  DCHECK(pointer_targets_.empty());
   SetMousePointerLocation(display_location, display_id);
   UpdateCursorProviderByLastKnownLocation();
   // Write our initial location back to our shared screen coordinate. This
diff --git a/services/ui/ws/server_window_compositor_frame_sink_manager.h b/services/ui/ws/server_window_compositor_frame_sink_manager.h
index d2d6887f5..2a200d29 100644
--- a/services/ui/ws/server_window_compositor_frame_sink_manager.h
+++ b/services/ui/ws/server_window_compositor_frame_sink_manager.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "cc/ipc/compositor_frame.mojom.h"
 #include "cc/ipc/frame_sink_manager.mojom.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index a7aa3ca..f45832f 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -32,6 +32,8 @@
     "blob/blob_transport_host.h",
     "blob/blob_transport_request_builder.cc",
     "blob/blob_transport_request_builder.h",
+    "blob/blob_transport_strategy.cc",
+    "blob/blob_transport_strategy.h",
     "blob/blob_url_request_job.cc",
     "blob/blob_url_request_job.h",
     "blob/blob_url_request_job_factory.cc",
@@ -243,6 +245,7 @@
     "blob/blob_storage_context_unittest.cc",
     "blob/blob_storage_registry_unittest.cc",
     "blob/blob_transport_request_builder_unittest.cc",
+    "blob/blob_transport_strategy_unittest.cc",
     "database/database_quota_client_unittest.cc",
     "database/database_tracker_unittest.cc",
     "database/database_util_unittest.cc",
@@ -309,6 +312,8 @@
     "test/fileapi_test_file_set.h",
     "test/mock_blob_url_request_context.cc",
     "test/mock_blob_url_request_context.h",
+    "test/mock_bytes_provider.cc",
+    "test/mock_bytes_provider.h",
     "test/mock_file_change_observer.cc",
     "test/mock_file_change_observer.h",
     "test/mock_file_update_observer.cc",
@@ -336,6 +341,7 @@
   deps = [
     ":browser",
     "//base/test:test_support",
+    "//mojo/common",
     "//net:test_support",
     "//testing/gtest",
     "//third_party/leveldatabase",
diff --git a/storage/browser/blob/blob_data_builder.cc b/storage/browser/blob/blob_data_builder.cc
index 204a4e8..fc2e6eb 100644
--- a/storage/browser/blob/blob_data_builder.cc
+++ b/storage/browser/blob/blob_data_builder.cc
@@ -111,8 +111,19 @@
                                          const char* data,
                                          size_t offset,
                                          size_t length) {
-  DCHECK_LT(index, items_.size());
   DCHECK(data);
+
+  char* target = GetFutureDataPointerToPopulate(index, offset, length);
+  if (!target)
+    return false;
+  std::memcpy(target, data, length);
+  return true;
+}
+
+char* BlobDataBuilder::GetFutureDataPointerToPopulate(size_t index,
+                                                      size_t offset,
+                                                      size_t length) {
+  DCHECK_LT(index, items_.size());
   DataElement* element = items_[index]->data_element_ptr();
 
   // We lazily allocate our data buffer by waiting until the first
@@ -128,16 +139,15 @@
   }
   if (element->type() != DataElement::TYPE_BYTES) {
     DVLOG(1) << "Invalid item type.";
-    return false;
+    return nullptr;
   }
   base::CheckedNumeric<size_t> checked_end = offset;
   checked_end += length;
   if (!checked_end.IsValid() || checked_end.ValueOrDie() > element->length()) {
     DVLOG(1) << "Invalid offset or length.";
-    return false;
+    return nullptr;
   }
-  std::memcpy(element->mutable_bytes() + offset, data, length);
-  return true;
+  return element->mutable_bytes() + offset;
 }
 
 size_t BlobDataBuilder::AppendFutureFile(uint64_t offset,
diff --git a/storage/browser/blob/blob_data_builder.h b/storage/browser/blob/blob_data_builder.h
index 088ceb755..56aebb2 100644
--- a/storage/browser/blob/blob_data_builder.h
+++ b/storage/browser/blob/blob_data_builder.h
@@ -81,6 +81,16 @@
                           size_t offset,
                           size_t length);
 
+  // Same as PopulateFutureData, but rather than passing in the data to be
+  // copied, this method returns a pointer where the caller can copy |length|
+  // bytes of data to.
+  // Returns nullptr if:
+  // * The item was not created by using AppendFutureData, or
+  // * The offset and length are not valid.
+  char* GetFutureDataPointerToPopulate(size_t index,
+                                       size_t offset,
+                                       size_t length);
+
   // Adds an item that is flagged for future data population. Use
   // 'PopulateFutureFile' to set the file path and expected modification time
   // of this file. Returns the index of the item (to be used in
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index fb8b8bd..67715d3 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -9,9 +9,44 @@
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/blob/blob_transport_strategy.h"
 
 namespace storage {
 
+namespace {
+
+using MemoryStrategy = BlobMemoryController::Strategy;
+
+bool CalculateBlobMemorySize(const std::vector<mojom::DataElementPtr>& elements,
+                             size_t* shortcut_bytes,
+                             uint64_t* total_bytes) {
+  DCHECK(shortcut_bytes);
+  DCHECK(total_bytes);
+
+  base::CheckedNumeric<uint64_t> total_size_checked = 0;
+  base::CheckedNumeric<size_t> shortcut_size_checked = 0;
+  for (const auto& e : elements) {
+    if (e->is_bytes()) {
+      const auto& bytes = e->get_bytes();
+      total_size_checked += bytes->length;
+      if (bytes->embedded_data) {
+        if (bytes->embedded_data->size() != bytes->length)
+          return false;
+        shortcut_size_checked += bytes->length;
+      }
+    } else {
+      continue;
+    }
+    if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid())
+      return false;
+  }
+  *shortcut_bytes = shortcut_size_checked.ValueOrDie();
+  *total_bytes = total_size_checked.ValueOrDie();
+  return true;
+}
+
+}  // namespace
+
 class BlobRegistryImpl::BlobUnderConstruction {
  public:
   BlobUnderConstruction(BlobRegistryImpl* blob_registry,
@@ -33,7 +68,7 @@
   // referenced by this new blob. This (and any further methods) could end up
   // deleting |this| by removing it from the blobs_under_construction_
   // collection in the blob service.
-  void StartFetchingBlobUUIDs();
+  void StartTransportation();
 
   ~BlobUnderConstruction() {}
 
@@ -48,9 +83,18 @@
   // Also deletes |this| by removing it from the blobs_under_construction_ list.
   void MarkAsBroken(BlobStatus reason,
                     const std::string& bad_message_reason = "") {
-    context()->CancelBuildingBlob(uuid(), reason);
+    DCHECK(BlobStatusIsError(reason));
+    DCHECK_EQ(bad_message_reason.empty(), !BlobStatusIsBadIPC(reason));
+    // The blob might no longer have any references, in which case it may no
+    // longer exist. If that happens just skip calling cancel.
+    if (context()->registry().HasEntry(uuid()))
+      context()->CancelBuildingBlob(uuid(), reason);
     if (!bad_message_reason.empty())
       std::move(bad_message_callback_).Run(bad_message_reason);
+    MarkAsFinishedAndDeleteSelf();
+  }
+
+  void MarkAsFinishedAndDeleteSelf() {
     blob_registry_->blobs_under_construction_.erase(uuid());
   }
 
@@ -75,6 +119,16 @@
   // transporting.
   void ResolvedAllBlobDependencies();
 
+  // Called when memory has been reserved for this blob and transport can begin.
+  // Could also be called if something caused the blob to become invalid before
+  // transportation began, in which case we just give up.
+  void OnReadyForTransport(
+      BlobStatus status,
+      std::vector<BlobMemoryController::FileCreationInfo> file_infos);
+
+  // Called when all data has been transported, or transport has failed.
+  void TransportComplete(BlobStatus result);
+
 #if DCHECK_IS_ON()
   // Returns true if the DAG made up by this blob and any other blobs that
   // are currently being built by BlobRegistryImpl contains any cycles.
@@ -99,6 +153,9 @@
   // called.
   mojo::ReportBadMessageCallback bad_message_callback_;
 
+  // Transport strategy to use when transporting data.
+  std::unique_ptr<BlobTransportStrategy> transport_strategy_;
+
   // List of UUIDs for referenced blobs. Same size as |elements_|. All entries
   // for non-blob elements will remain empty strings.
   std::vector<std::string> referenced_blob_uuids_;
@@ -113,17 +170,11 @@
   DISALLOW_COPY_AND_ASSIGN(BlobUnderConstruction);
 };
 
-void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() {
+void BlobRegistryImpl::BlobUnderConstruction::StartTransportation() {
   size_t blob_count = 0;
   for (size_t i = 0; i < elements_.size(); ++i) {
     const auto& element = elements_[i];
     if (element->is_blob()) {
-      if (element->get_blob()->blob.encountered_error()) {
-        // Will delete |this|.
-        MarkAsBroken(BlobStatus::ERR_REFERENCED_BLOB_BROKEN);
-        return;
-      }
-
       // If connection to blob is broken, something bad happened, so mark this
       // new blob as broken, which will delete |this| and keep it from doing
       // unneeded extra work.
@@ -134,6 +185,10 @@
       element->get_blob()->blob->GetInternalUUID(
           base::BindOnce(&BlobUnderConstruction::ReceivedBlobUUID,
                          weak_ptr_factory_.GetWeakPtr(), blob_count++));
+    } else if (element->is_bytes()) {
+      element->get_bytes()->data.set_connection_error_handler(base::BindOnce(
+          &BlobUnderConstruction::MarkAsBroken, weak_ptr_factory_.GetWeakPtr(),
+          BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, ""));
     }
   }
   referenced_blob_uuids_.resize(blob_count);
@@ -142,6 +197,32 @@
   // Without it a blob could forever remaing pending if a renderer sends us
   // a BlobPtr connected to a (malicious) non-responding implementation.
 
+  // Do some basic validation of bytes to transport, and determine memory
+  // transport strategy to use later.
+  uint64_t transport_memory_size = 0;
+  size_t shortcut_size = 0;
+  if (!CalculateBlobMemorySize(elements_, &shortcut_size,
+                               &transport_memory_size)) {
+    MarkAsBroken(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+                 "Invalid byte element sizes in BlobRegistry::Register");
+    return;
+  }
+
+  const BlobMemoryController& memory_controller =
+      context()->memory_controller();
+  MemoryStrategy memory_strategy =
+      memory_controller.DetermineStrategy(shortcut_size, transport_memory_size);
+  if (memory_strategy == MemoryStrategy::TOO_LARGE) {
+    MarkAsBroken(BlobStatus::ERR_OUT_OF_MEMORY);
+    return;
+  }
+
+  transport_strategy_ = BlobTransportStrategy::Create(
+      memory_strategy, &builder_,
+      base::BindOnce(&BlobUnderConstruction::TransportComplete,
+                     weak_ptr_factory_.GetWeakPtr()),
+      memory_controller.limits());
+
   // If there were no unresolved blobs, immediately proceed to the next step.
   // Currently this will only happen if there are no blobs referenced
   // whatsoever, but hopefully in the future blob UUIDs will be cached in the
@@ -212,10 +293,11 @@
   DCHECK_EQ(resolved_blob_uuid_count_, referenced_blob_uuids_.size());
   DCHECK_EQ(ready_dependent_blob_count_, referenced_blob_uuids_.size());
 
-  // TODO(mek): Fill BlobDataBuilder with elements_ other than blobs.
   auto blob_uuid_it = referenced_blob_uuids_.begin();
   for (const auto& element : elements_) {
-    if (element->is_file()) {
+    if (element->is_bytes()) {
+      transport_strategy_->AddBytesElement(element->get_bytes().get());
+    } else if (element->is_file()) {
       const auto& f = element->get_file();
       builder_.AppendFile(f->path, f->offset, f->length,
                           f->expected_modification_time.value_or(base::Time()));
@@ -231,14 +313,49 @@
                           element->get_blob()->length);
     }
   }
+  // OnReadyForTransport can be called synchronously, which can call
+  // MarkAsFinishedAndDeleteSelf synchronously, so don't access any members
+  // after this call.
   std::unique_ptr<BlobDataHandle> new_handle =
       context()->BuildPreregisteredBlob(
-          builder_, BlobStorageContext::TransportAllowedCallback());
+          builder_, base::Bind(&BlobUnderConstruction::OnReadyForTransport,
+                               weak_ptr_factory_.GetWeakPtr()));
 
   // TODO(mek): Update BlobImpl with new BlobDataHandle. Although handles
   // only differ in their size() attribute, which is currently not used by
   // BlobImpl.
-  DCHECK(!BlobStatusIsPending(new_handle->GetBlobStatus()));
+}
+
+void BlobRegistryImpl::BlobUnderConstruction::OnReadyForTransport(
+    BlobStatus status,
+    std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
+  if (!BlobStatusIsPending(status)) {
+    // Done or error.
+    MarkAsFinishedAndDeleteSelf();
+    return;
+  }
+  transport_strategy_->BeginTransport(std::move(file_infos));
+}
+
+void BlobRegistryImpl::BlobUnderConstruction::TransportComplete(
+    BlobStatus result) {
+  // The blob might no longer have any references, in which case it may no
+  // longer exist. If that happens just skip calling Complete.
+  // TODO(mek): Stop building sooner if a blob is no longer referenced.
+  if (context()->registry().HasEntry(uuid())) {
+    if (result == BlobStatus::DONE)
+      context()->NotifyTransportComplete(uuid());
+    else
+      context()->CancelBuildingBlob(uuid(), result);
+  }
+  if (BlobStatusIsBadIPC(result)) {
+    // BlobTransportStrategy might have already reported a BadMessage on the
+    // BytesProvider binding, but just to be safe, also report one on the
+    // BlobRegistry binding itself.
+    std::move(bad_message_callback_)
+        .Run("Received invalid data while transporting blob");
+  }
+  MarkAsFinishedAndDeleteSelf();
 }
 
 #if DCHECK_IS_ON()
@@ -331,7 +448,7 @@
       context_->AddFutureBlob(uuid, content_type, content_disposition);
   BlobImpl::Create(std::move(handle), std::move(blob));
 
-  blobs_under_construction_[uuid]->StartFetchingBlobUUIDs();
+  blobs_under_construction_[uuid]->StartTransportation();
 
   std::move(callback).Run();
 }
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index 0f2a60b2..e31518bd 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -4,13 +4,18 @@
 
 #include "storage/browser/blob/blob_registry_impl.h"
 
+#include <limits>
 #include "base/files/scoped_temp_dir.h"
+#include "base/rand_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/test/mock_bytes_provider.h"
 #include "storage/browser/test/mock_special_storage_policy.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,6 +23,15 @@
 
 namespace {
 
+const size_t kTestBlobStorageIPCThresholdBytes = 5;
+const size_t kTestBlobStorageMaxSharedMemoryBytes = 20;
+const size_t kTestBlobStorageMaxBytesDataItemSize = 13;
+
+const size_t kTestBlobStorageMaxBlobMemorySize = 400;
+const uint64_t kTestBlobStorageMaxDiskSpace = 4000;
+const uint64_t kTestBlobStorageMinFileSizeBytes = 10;
+const uint64_t kTestBlobStorageMaxFileSizeBytes = 100;
+
 class MockBlob : public mojom::Blob {
  public:
   explicit MockBlob(const std::string& uuid) : uuid_(uuid) {}
@@ -51,13 +65,20 @@
   bool can_read_file_system_file_result = true;
 };
 
+void BindBytesProvider(std::unique_ptr<MockBytesProvider> impl,
+                       mojom::BytesProviderRequest request) {
+  mojo::MakeStrongBinding(std::move(impl), std::move(request));
+}
+
 }  // namespace
 
 class BlobRegistryImplTest : public testing::Test {
  public:
   void SetUp() override {
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
-    context_ = base::MakeUnique<BlobStorageContext>();
+    context_ = base::MakeUnique<BlobStorageContext>(
+        data_dir_.GetPath(),
+        base::CreateTaskRunnerWithTraits({base::MayBlock()}));
     auto storage_policy =
         base::MakeRefCounted<content::MockSpecialStoragePolicy>();
     file_system_context_ = base::MakeRefCounted<storage::FileSystemContext>(
@@ -74,11 +95,28 @@
     auto delegate = base::MakeUnique<MockDelegate>();
     delegate_ptr_ = delegate.get();
     registry_impl_->Bind(MakeRequest(&registry_), std::move(delegate));
+
     mojo::edk::SetDefaultProcessErrorCallback(base::Bind(
         &BlobRegistryImplTest::OnBadMessage, base::Unretained(this)));
+
+    storage::BlobStorageLimits limits;
+    limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes;
+    limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes;
+    limits.max_bytes_data_item_size = kTestBlobStorageMaxBytesDataItemSize;
+    limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize;
+    limits.desired_max_disk_space = kTestBlobStorageMaxDiskSpace;
+    limits.effective_max_disk_space = kTestBlobStorageMaxDiskSpace;
+    limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes;
+    limits.max_file_size = kTestBlobStorageMaxFileSizeBytes;
+    context_->mutable_memory_controller()->set_limits_for_testing(limits);
+
+    // Disallow IO on the main loop.
+    base::ThreadRestrictions::SetIOAllowed(false);
   }
 
   void TearDown() override {
+    base::ThreadRestrictions::SetIOAllowed(true);
+
     mojo::edk::SetDefaultProcessErrorCallback(
         mojo::edk::ProcessErrorCallback());
   }
@@ -118,6 +156,35 @@
     loop.Run();
   }
 
+  mojom::BytesProviderPtr CreateBytesProvider(const std::string& bytes) {
+    if (!bytes_provider_runner_) {
+      bytes_provider_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::WithBaseSyncPrimitives()});
+    }
+    mojom::BytesProviderPtr result;
+    auto provider = base::MakeUnique<MockBytesProvider>(
+        std::vector<uint8_t>(bytes.begin(), bytes.end()), &reply_request_count_,
+        &stream_request_count_, &file_request_count_);
+    bytes_provider_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&BindBytesProvider, std::move(provider),
+                                  MakeRequest(&result)));
+    return result;
+  }
+
+  void CreateBytesProvider(const std::string& bytes,
+                           mojom::BytesProviderRequest request) {
+    if (!bytes_provider_runner_) {
+      bytes_provider_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::WithBaseSyncPrimitives()});
+    }
+    auto provider = base::MakeUnique<MockBytesProvider>(
+        std::vector<uint8_t>(bytes.begin(), bytes.end()), &reply_request_count_,
+        &stream_request_count_, &file_request_count_);
+    bytes_provider_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&BindBytesProvider, std::move(provider),
+                                  std::move(request)));
+  }
+
  protected:
   base::ScopedTempDir data_dir_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -126,6 +193,11 @@
   std::unique_ptr<BlobRegistryImpl> registry_impl_;
   mojom::BlobRegistryPtr registry_;
   MockDelegate* delegate_ptr_;
+  scoped_refptr<base::SequencedTaskRunner> bytes_provider_runner_;
+
+  size_t reply_request_count_ = 0;
+  size_t stream_request_count_ = 0;
+  size_t file_request_count_ = 0;
 
   std::vector<std::string> bad_messages_;
 };
@@ -389,10 +461,10 @@
   WaitForBlobCompletion(handle3.get());
 
   EXPECT_FALSE(handle2->IsBroken());
-  EXPECT_EQ(BlobStatus::DONE, handle2->GetBlobStatus());
+  ASSERT_EQ(BlobStatus::DONE, handle2->GetBlobStatus());
 
   EXPECT_FALSE(handle3->IsBroken());
-  EXPECT_EQ(BlobStatus::DONE, handle3->GetBlobStatus());
+  ASSERT_EQ(BlobStatus::DONE, handle3->GetBlobStatus());
 
   BlobDataBuilder expected_blob_data(kId2);
   expected_blob_data.AppendData("hello wo");
@@ -441,7 +513,7 @@
   WaitForBlobCompletion(handle.get());
 
   EXPECT_FALSE(handle->IsBroken());
-  EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
 
   BlobDataBuilder expected_blob_data(kId);
   expected_blob_data.AppendFile(path, 0, 16, base::Time());
@@ -510,7 +582,7 @@
   WaitForBlobCompletion(handle.get());
 
   EXPECT_FALSE(handle->IsBroken());
-  EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
 
   BlobDataBuilder expected_blob_data(kId);
   expected_blob_data.AppendFileSystemFile(url, 0, 16, base::Time());
@@ -518,4 +590,305 @@
   EXPECT_EQ(expected_blob_data, *handle->CreateSnapshot());
 }
 
+TEST_F(BlobRegistryImplTest, Register_BytesInvalidEmbeddedData) {
+  const std::string kId = "id";
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      10, std::vector<uint8_t>(5), CreateBytesProvider(""))));
+
+  mojom::BlobPtr blob;
+  EXPECT_FALSE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                   std::move(elements)));
+  EXPECT_EQ(1u, bad_messages_.size());
+
+  registry_.FlushForTesting();
+  EXPECT_TRUE(registry_.encountered_error());
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_TRUE(handle->IsBroken());
+  EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+            handle->GetBlobStatus());
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_BytesInvalidDataSize) {
+  const std::string kId = "id";
+
+  // Two elements that together are more than uint64_t::max bytes.
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(
+      mojom::DataElementBytes::New(8, base::nullopt, CreateBytesProvider(""))));
+  elements.push_back(mojom::DataElement::NewBytes(
+      mojom::DataElementBytes::New(std::numeric_limits<uint64_t>::max() - 4,
+                                   base::nullopt, CreateBytesProvider(""))));
+
+  mojom::BlobPtr blob;
+  EXPECT_FALSE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                   std::move(elements)));
+  EXPECT_EQ(1u, bad_messages_.size());
+
+  registry_.FlushForTesting();
+  EXPECT_TRUE(registry_.encountered_error());
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_TRUE(handle->IsBroken());
+  EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+            handle->GetBlobStatus());
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_BytesOutOfMemory) {
+  const std::string kId = "id";
+
+  // Two elements that together don't fit in the test quota.
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kTestBlobStorageMaxDiskSpace, base::nullopt, CreateBytesProvider(""))));
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kTestBlobStorageMaxDiskSpace, base::nullopt, CreateBytesProvider(""))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_TRUE(handle->IsBroken());
+  EXPECT_EQ(BlobStatus::ERR_OUT_OF_MEMORY, handle->GetBlobStatus());
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_ValidEmbeddedBytes) {
+  const std::string kId = "id";
+  const std::string kData = "hello world";
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kData.size(), std::vector<uint8_t>(kData.begin(), kData.end()),
+      CreateBytesProvider(kData))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_FALSE(handle->IsBroken());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+
+  BlobDataBuilder expected_blob_data(kId);
+  expected_blob_data.AppendData(kData);
+
+  EXPECT_EQ(expected_blob_data, *handle->CreateSnapshot());
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_ValidBytesAsReply) {
+  const std::string kId = "id";
+  const std::string kData = "hello";
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kData.size(), base::nullopt, CreateBytesProvider(kData))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_FALSE(handle->IsBroken());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+
+  BlobDataBuilder expected_blob_data(kId);
+  expected_blob_data.AppendData(kData);
+
+  EXPECT_EQ(expected_blob_data, *handle->CreateSnapshot());
+
+  EXPECT_EQ(1u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_ValidBytesAsStream) {
+  const std::string kId = "id";
+  const std::string kData =
+      base::RandBytesAsString(kTestBlobStorageMaxSharedMemoryBytes * 3 + 13);
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kData.size(), base::nullopt, CreateBytesProvider(kData))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_FALSE(handle->IsBroken());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+
+  size_t offset = 0;
+  BlobDataBuilder expected_blob_data(kId);
+  while (offset < kData.size()) {
+    expected_blob_data.AppendData(
+        kData.substr(offset, kTestBlobStorageMaxBytesDataItemSize));
+    offset += kTestBlobStorageMaxBytesDataItemSize;
+  }
+
+  EXPECT_EQ(expected_blob_data, *handle->CreateSnapshot());
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(1u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobRegistryImplTest, Register_ValidBytesAsFile) {
+  const std::string kId = "id";
+  const std::string kData =
+      base::RandBytesAsString(kTestBlobStorageMaxBlobMemorySize + 42);
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kData.size(), base::nullopt, CreateBytesProvider(kData))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_FALSE(handle->IsBroken());
+  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
+
+  BlobDataBuilder expected_blob_data(kId);
+  expected_blob_data.AppendData(kData);
+
+  size_t expected_file_count =
+      1 + kData.size() / kTestBlobStorageMaxFileSizeBytes;
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(expected_file_count, file_request_count_);
+
+  auto snapshot = handle->CreateSnapshot();
+  EXPECT_EQ(expected_file_count, snapshot->items().size());
+  size_t remaining_size = kData.size();
+  for (const auto& item : snapshot->items()) {
+    EXPECT_EQ(DataElement::TYPE_FILE, item->type());
+    EXPECT_EQ(0u, item->offset());
+    if (remaining_size > kTestBlobStorageMaxFileSizeBytes)
+      EXPECT_EQ(kTestBlobStorageMaxFileSizeBytes, item->length());
+    else
+      EXPECT_EQ(remaining_size, item->length());
+    remaining_size -= item->length();
+  }
+  EXPECT_EQ(0u, remaining_size);
+}
+
+TEST_F(BlobRegistryImplTest, Register_BytesProviderClosedPipe) {
+  const std::string kId = "id";
+
+  mojom::BytesProviderPtr bytes_provider;
+  MakeRequest(&bytes_provider);
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      32, base::nullopt, std::move(bytes_provider))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+  EXPECT_TRUE(bad_messages_.empty());
+
+  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+  WaitForBlobCompletion(handle.get());
+
+  EXPECT_TRUE(handle->IsBroken());
+  EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, handle->GetBlobStatus());
+}
+
+TEST_F(BlobRegistryImplTest,
+       Register_DefereferencedWhileBuildingBeforeBreaking) {
+  const std::string kId = "id";
+
+  mojom::BytesProviderPtr bytes_provider;
+  mojom::BytesProviderRequest request = MakeRequest(&bytes_provider);
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      32, base::nullopt, std::move(bytes_provider))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+  EXPECT_TRUE(bad_messages_.empty());
+
+  EXPECT_TRUE(context_->registry().HasEntry(kId));
+  EXPECT_TRUE(context_->GetBlobDataFromUUID(kId)->IsBeingBuilt());
+
+  // Now drop all references to the blob.
+  blob.reset();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(context_->registry().HasEntry(kId));
+
+  // Now cause construction to fail, if it would still be going on.
+  request = nullptr;
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(BlobRegistryImplTest,
+       Register_DefereferencedWhileBuildingBeforeTransporting) {
+  const std::string kId = "id";
+  const std::string kData = "hello world";
+
+  mojom::BytesProviderPtr bytes_provider;
+  mojom::BytesProviderRequest request = MakeRequest(&bytes_provider);
+
+  std::vector<mojom::DataElementPtr> elements;
+  elements.push_back(mojom::DataElement::NewBytes(mojom::DataElementBytes::New(
+      kData.size(), base::nullopt, std::move(bytes_provider))));
+
+  mojom::BlobPtr blob;
+  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
+                                  std::move(elements)));
+  EXPECT_TRUE(bad_messages_.empty());
+
+  EXPECT_TRUE(context_->registry().HasEntry(kId));
+  EXPECT_TRUE(context_->GetBlobDataFromUUID(kId)->IsBeingBuilt());
+
+  // Now drop all references to the blob.
+  blob.reset();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(context_->registry().HasEntry(kId));
+
+  // Now cause construction to complete, if it would still be going on.
+  CreateBytesProvider(kData, std::move(request));
+  scoped_task_environment_.RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace storage
diff --git a/storage/browser/blob/blob_storage_context.h b/storage/browser/blob/blob_storage_context.h
index a923cdb5..52e09b2 100644
--- a/storage/browser/blob/blob_storage_context.h
+++ b/storage/browser/blob/blob_storage_context.h
@@ -149,6 +149,7 @@
   friend class BlobDataHandle;
   friend class BlobDataHandle::BlobDataHandleShared;
   friend class BlobFlattenerTest;
+  friend class BlobRegistryImplTest;
   friend class BlobSliceTest;
   friend class BlobStorageContextTest;
 
diff --git a/storage/browser/blob/blob_transport_strategy.cc b/storage/browser/blob/blob_transport_strategy.cc
new file mode 100644
index 0000000..4d2502cd
--- /dev/null
+++ b/storage/browser/blob/blob_transport_strategy.cc
@@ -0,0 +1,386 @@
+// 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 "storage/browser/blob/blob_transport_strategy.h"
+
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/public/interfaces/blobs.mojom.h"
+
+namespace storage {
+
+namespace {
+
+using MemoryStrategy = BlobMemoryController::Strategy;
+
+// Transport strategy when no transport is needed. All Bytes elements should
+// have their data embedded already.
+class NoneNeededTransportStrategy : public BlobTransportStrategy {
+ public:
+  NoneNeededTransportStrategy(BlobDataBuilder* builder,
+                              ResultCallback result_callback)
+      : BlobTransportStrategy(builder, std::move(result_callback)) {}
+
+  void AddBytesElement(mojom::DataElementBytes* bytes) override {
+    DCHECK(bytes->embedded_data);
+    DCHECK_EQ(bytes->length, bytes->embedded_data->size());
+    builder_->AppendData(
+        reinterpret_cast<const char*>(bytes->embedded_data->data()),
+        bytes->length);
+  }
+
+  void BeginTransport(
+      std::vector<BlobMemoryController::FileCreationInfo>) override {
+    std::move(result_callback_).Run(BlobStatus::DONE);
+  }
+};
+
+// Transport strategy that requests all data as replies.
+class ReplyTransportStrategy : public BlobTransportStrategy {
+ public:
+  ReplyTransportStrategy(BlobDataBuilder* builder,
+                         ResultCallback result_callback)
+      : BlobTransportStrategy(builder, std::move(result_callback)) {}
+
+  void AddBytesElement(mojom::DataElementBytes* bytes) override {
+    size_t builder_element_index = builder_->AppendFutureData(bytes->length);
+    // base::Unretained is safe because |this| is guaranteed (by the contract
+    // that code using BlobTransportStrategy should adhere to) to outlive the
+    // BytesProvider.
+    requests_.push_back(base::BindOnce(
+        &mojom::BytesProvider::RequestAsReply,
+        base::Unretained(bytes->data.get()),
+        base::BindOnce(&ReplyTransportStrategy::OnReply, base::Unretained(this),
+                       builder_element_index, bytes->length)));
+  }
+
+  void BeginTransport(
+      std::vector<BlobMemoryController::FileCreationInfo>) override {
+    if (requests_.empty()) {
+      std::move(result_callback_).Run(BlobStatus::DONE);
+      return;
+    }
+    for (auto& request : requests_)
+      std::move(request).Run();
+  }
+
+ private:
+  void OnReply(size_t builder_element_index,
+               size_t expected_size,
+               const std::vector<uint8_t>& data) {
+    if (data.size() != expected_size) {
+      mojo::ReportBadMessage(
+          "Invalid data size in reply to BytesProvider::RequestAsReply");
+      std::move(result_callback_)
+          .Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+      return;
+    }
+    bool populate_result = builder_->PopulateFutureData(
+        builder_element_index, reinterpret_cast<const char*>(data.data()), 0,
+        data.size());
+    DCHECK(populate_result);
+
+    if (++num_resolved_requests_ == requests_.size())
+      std::move(result_callback_).Run(BlobStatus::DONE);
+  }
+
+  std::vector<base::OnceClosure> requests_;
+  size_t num_resolved_requests_ = 0;
+};
+
+// Transport strategy that requests all data as data pipes, one pipe at a time.
+class DataPipeTransportStrategy : public BlobTransportStrategy {
+ public:
+  DataPipeTransportStrategy(BlobDataBuilder* builder,
+                            ResultCallback result_callback,
+                            const BlobStorageLimits& limits)
+      : BlobTransportStrategy(builder, std::move(result_callback)),
+        limits_(limits),
+        watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {}
+
+  void AddBytesElement(mojom::DataElementBytes* bytes) override {
+    // Split up the data in |max_bytes_data_item_size| sized chunks.
+    for (uint64_t source_offset = 0; source_offset < bytes->length;
+         source_offset += limits_.max_bytes_data_item_size) {
+      size_t builder_element_index =
+          builder_->AppendFutureData(std::min<uint64_t>(
+              bytes->length - source_offset, limits_.max_bytes_data_item_size));
+      if (source_offset == 0) {
+        requests_.push_back(base::BindOnce(
+            &DataPipeTransportStrategy::RequestDataPipe, base::Unretained(this),
+            bytes->data.get(), bytes->length, builder_element_index));
+      }
+    }
+  }
+
+  void BeginTransport(
+      std::vector<BlobMemoryController::FileCreationInfo>) override {
+    NextRequestOrDone();
+  }
+
+ private:
+  void NextRequestOrDone() {
+    if (requests_.empty()) {
+      std::move(result_callback_).Run(BlobStatus::DONE);
+      return;
+    }
+    auto request = std::move(requests_.front());
+    requests_.pop_front();
+    std::move(request).Run();
+  }
+
+  void RequestDataPipe(mojom::BytesProvider* provider,
+                       size_t expected_source_size,
+                       size_t first_builder_element_index) {
+    // TODO(mek): Determine if the overhead of creating a new SharedMemory
+    // segment for each BytesProvider is too much. If it is possible solutions
+    // would include somehow teaching DataPipe to reuse the SharedMemory from a
+    // previous DataPipe, or simply using a single BytesProvider for all bytes
+    // elements. http://crbug.com/741159
+    DCHECK(!consumer_handle_.is_valid());
+    mojo::ScopedDataPipeProducerHandle producer_handle;
+    MojoCreateDataPipeOptions options;
+    options.struct_size = sizeof(MojoCreateDataPipeOptions);
+    options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+    options.element_num_bytes = 1;
+    options.capacity_num_bytes =
+        std::min(expected_source_size, limits_.max_shared_memory_size);
+    MojoResult result =
+        CreateDataPipe(&options, &producer_handle, &consumer_handle_);
+    if (result != MOJO_RESULT_OK) {
+      DVLOG(1) << "Unable to create data pipe for blob transfer.";
+      std::move(result_callback_).Run(BlobStatus::ERR_OUT_OF_MEMORY);
+      return;
+    }
+
+    current_source_offset_ = 0;
+    provider->RequestAsStream(std::move(producer_handle));
+    watcher_.Watch(consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                   base::Bind(&DataPipeTransportStrategy::OnDataPipeReadable,
+                              base::Unretained(this), expected_source_size,
+                              first_builder_element_index));
+  }
+
+  void OnDataPipeReadable(size_t expected_full_source_size,
+                          size_t first_builder_element_index,
+                          MojoResult result) {
+    // The index of the element data should currently be written to, relative to
+    // the first element of this stream (first_builder_element_index).
+    size_t relative_element_index =
+        current_source_offset_ / limits_.max_bytes_data_item_size;
+    // The offset into the current element where data should be written next.
+    size_t offset_in_builder_element =
+        current_source_offset_ -
+        relative_element_index * limits_.max_bytes_data_item_size;
+
+    while (true) {
+      uint32_t num_bytes = 0;
+      const void* source_buffer;
+      MojoResult read_result =
+          mojo::BeginReadDataRaw(consumer_handle_.get(), &source_buffer,
+                                 &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+      if (read_result == MOJO_RESULT_SHOULD_WAIT)
+        return;
+      if (read_result != MOJO_RESULT_OK) {
+        // Data pipe broke before we received all the data.
+        std::move(result_callback_).Run(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT);
+        return;
+      }
+
+      if (current_source_offset_ + num_bytes > expected_full_source_size) {
+        // Received more bytes then expected.
+        std::move(result_callback_)
+            .Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+        return;
+      }
+
+      // Only read as many bytes as can fit in current data element. Any
+      // remaining bytes will be read on the next iteration of this loop.
+      num_bytes =
+          std::min<uint32_t>(num_bytes, limits_.max_bytes_data_item_size -
+                                            offset_in_builder_element);
+      char* output_buffer = builder_->GetFutureDataPointerToPopulate(
+          first_builder_element_index + relative_element_index,
+          offset_in_builder_element, num_bytes);
+      DCHECK(output_buffer);
+
+      std::memcpy(output_buffer, source_buffer, num_bytes);
+      read_result = mojo::EndReadDataRaw(consumer_handle_.get(), num_bytes);
+      DCHECK_EQ(read_result, MOJO_RESULT_OK);
+
+      current_source_offset_ += num_bytes;
+      if (current_source_offset_ >= expected_full_source_size) {
+        // Done with this stream, on to the next.
+        // TODO(mek): Should this wait to see if more data than expected gets
+        // written, instead of immediately closing the pipe?
+        watcher_.Cancel();
+        consumer_handle_.reset();
+        NextRequestOrDone();
+        return;
+      }
+
+      offset_in_builder_element += num_bytes;
+      if (offset_in_builder_element >= limits_.max_bytes_data_item_size) {
+        offset_in_builder_element = 0;
+        relative_element_index++;
+      }
+    }
+  }
+
+  const BlobStorageLimits& limits_;
+  std::deque<base::OnceClosure> requests_;
+
+  mojo::ScopedDataPipeConsumerHandle consumer_handle_;
+  mojo::SimpleWatcher watcher_;
+  // How many bytes have been read and processed so far from the current data
+  // pipe.
+  size_t current_source_offset_ = 0;
+};
+
+// Transport strategy that requests all data through files.
+class FileTransportStrategy : public BlobTransportStrategy {
+ public:
+  FileTransportStrategy(BlobDataBuilder* builder,
+                        ResultCallback result_callback,
+                        const BlobStorageLimits& limits)
+      : BlobTransportStrategy(builder, std::move(result_callback)),
+        limits_(limits) {}
+
+  void AddBytesElement(mojom::DataElementBytes* bytes) override {
+    uint64_t source_offset = 0;
+    while (source_offset < bytes->length) {
+      if (current_file_size_ >= limits_.max_file_size ||
+          file_requests_.empty()) {
+        current_file_size_ = 0;
+        current_file_index_++;
+        file_requests_.push_back(std::vector<Request>());
+      }
+
+      // Make sure no single file gets too big, but do use up all the available
+      // space in all but the last file.
+      uint64_t element_size =
+          std::min(bytes->length - source_offset,
+                   limits_.max_file_size - current_file_size_);
+      size_t builder_element_index = builder_->AppendFutureFile(
+          current_file_size_, element_size, file_requests_.size() - 1);
+
+      num_unresolved_requests_++;
+      file_requests_.back().push_back(Request{bytes->data.get(), source_offset,
+                                              element_size,
+                                              builder_element_index});
+
+      source_offset += element_size;
+      current_file_size_ += element_size;
+    }
+  }
+
+  void BeginTransport(
+      std::vector<BlobMemoryController::FileCreationInfo> file_infos) override {
+    if (file_requests_.empty()) {
+      std::move(result_callback_).Run(BlobStatus::DONE);
+      return;
+    }
+    DCHECK_EQ(file_infos.size(), file_requests_.size());
+    for (size_t file_index = 0; file_index < file_requests_.size();
+         ++file_index) {
+      const auto& requests = file_requests_[file_index];
+      uint64_t file_offset = 0;
+      for (size_t i = 0; i < requests.size(); ++i) {
+        const auto& request = requests[i];
+        base::File file = i == requests.size() - 1
+                              ? std::move(file_infos[file_index].file)
+                              : file_infos[file_index].file.Duplicate();
+        // base::Unretained is safe because |this| is guaranteed (by the
+        // contract that code using BlobTransportStrategy should adhere to) to
+        // outlive the BytesProvider.
+        request.provider->RequestAsFile(
+            request.source_offset, request.source_size, std::move(file),
+            file_offset,
+            base::BindOnce(&FileTransportStrategy::OnReply,
+                           base::Unretained(this),
+                           request.builder_element_index,
+                           file_infos[file_index].file_reference));
+        file_offset += request.source_size;
+      }
+    }
+  }
+
+ private:
+  void OnReply(size_t builder_element_index,
+               const scoped_refptr<ShareableFileReference>& file_reference,
+               base::Optional<base::Time> time_file_modified) {
+    if (!time_file_modified) {
+      // Writing to the file failed in the renderer.
+      std::move(result_callback_).Run(BlobStatus::ERR_FILE_WRITE_FAILED);
+      return;
+    }
+
+    bool populate_result = builder_->PopulateFutureFile(
+        builder_element_index, file_reference, *time_file_modified);
+    DCHECK(populate_result);
+
+    if (--num_unresolved_requests_ == 0)
+      std::move(result_callback_).Run(BlobStatus::DONE);
+  }
+
+  const BlobStorageLimits& limits_;
+
+  // State used to assign bytes elements to individual files.
+  // The index of the first file that still has available space.
+  size_t current_file_index_ = 0;
+  // How big the current file already is.
+  uint64_t current_file_size_ = 0;
+
+  struct Request {
+    // The BytesProvider to request this particular bit of data from.
+    mojom::BytesProvider* provider;
+    // Offset into the BytesProvider of the data to request.
+    uint64_t source_offset;
+    // Size of the bytes to request.
+    uint64_t source_size;
+    // Index of the element in the BlobDataBuilder the data should be populated
+    // into.
+    size_t builder_element_index;
+  };
+  // For each file, a list of requests involving that file.
+  std::vector<std::vector<Request>> file_requests_;
+
+  size_t num_unresolved_requests_ = 0;
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<BlobTransportStrategy> BlobTransportStrategy::Create(
+    MemoryStrategy strategy,
+    BlobDataBuilder* builder,
+    ResultCallback result_callback,
+    const BlobStorageLimits& limits) {
+  switch (strategy) {
+    case MemoryStrategy::NONE_NEEDED:
+      return base::MakeUnique<NoneNeededTransportStrategy>(
+          builder, std::move(result_callback));
+    case MemoryStrategy::IPC:
+      return base::MakeUnique<ReplyTransportStrategy>(
+          builder, std::move(result_callback));
+    case MemoryStrategy::SHARED_MEMORY:
+      return base::MakeUnique<DataPipeTransportStrategy>(
+          builder, std::move(result_callback), limits);
+    case MemoryStrategy::FILE:
+      return base::MakeUnique<FileTransportStrategy>(
+          builder, std::move(result_callback), limits);
+    case MemoryStrategy::TOO_LARGE:
+      NOTREACHED();
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+BlobTransportStrategy::~BlobTransportStrategy() {}
+
+BlobTransportStrategy::BlobTransportStrategy(BlobDataBuilder* builder,
+                                             ResultCallback result_callback)
+    : builder_(builder), result_callback_(std::move(result_callback)) {}
+
+}  // namespace storage
diff --git a/storage/browser/blob/blob_transport_strategy.h b/storage/browser/blob/blob_transport_strategy.h
new file mode 100644
index 0000000..fdc35e6
--- /dev/null
+++ b/storage/browser/blob/blob_transport_strategy.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
+#define STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
+
+#include "base/callback.h"
+#include "storage/browser/blob/blob_memory_controller.h"
+#include "storage/browser/storage_browser_export.h"
+
+namespace storage {
+
+class BlobDataBuilder;
+
+namespace mojom {
+class DataElementBytes;
+}
+
+// This class is responsible for transporting bytes for an under-construction
+// blob, using a specified transport strategy. This is used by BlobRegistryImpl
+// for the actual transportation of bytes.
+class STORAGE_EXPORT BlobTransportStrategy {
+ public:
+  using ResultCallback = base::OnceCallback<void(BlobStatus)>;
+
+  // Creates a BlobTransportStrategy instance for the specified memory strategy.
+  // The BlobDataBuilder and BlobStorageLimits must outlive the returned
+  // BlobTransportStrategy.
+  static std::unique_ptr<BlobTransportStrategy> Create(
+      BlobMemoryController::Strategy strategy,
+      BlobDataBuilder* builder,
+      ResultCallback result_callback,
+      const BlobStorageLimits& limits);
+  virtual ~BlobTransportStrategy();
+
+  // Called once for each DataElementBytes in a blob. The |bytes| passed in must
+  // outlive the BlobTransportStrategy instance.
+  virtual void AddBytesElement(mojom::DataElementBytes* bytes) = 0;
+
+  // Called when quota has been allocated and transportation should begin.
+  // Implementations will call the |result_callback_| when transportation has
+  // completed, or failed.
+  virtual void BeginTransport(
+      std::vector<BlobMemoryController::FileCreationInfo> file_infos) = 0;
+
+ protected:
+  BlobTransportStrategy(BlobDataBuilder* builder,
+                        ResultCallback result_callback);
+
+  BlobDataBuilder* builder_;
+  ResultCallback result_callback_;
+};
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
diff --git a/storage/browser/blob/blob_transport_strategy_unittest.cc b/storage/browser/blob/blob_transport_strategy_unittest.cc
new file mode 100644
index 0000000..d876403
--- /dev/null
+++ b/storage/browser/blob/blob_transport_strategy_unittest.cc
@@ -0,0 +1,520 @@
+// 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 "storage/browser/blob/blob_transport_strategy.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/rand_util.h"
+#include "base/run_loop.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_restrictions.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/test/mock_bytes_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace storage {
+
+namespace {
+
+using MemoryStrategy = BlobMemoryController::Strategy;
+using FileInfoVector = std::vector<BlobMemoryController::FileCreationInfo>;
+
+const size_t kTestBlobStorageIPCThresholdBytes = 5;
+const size_t kTestBlobStorageMaxSharedMemoryBytes = 20;
+const size_t kTestBlobStorageMaxBytesDataItemSize = 13;
+
+const size_t kTestBlobStorageMaxBlobMemorySize = 400;
+const uint64_t kTestBlobStorageMaxDiskSpace = 4000;
+const uint64_t kTestBlobStorageMinFileSizeBytes = 10;
+const uint64_t kTestBlobStorageMaxFileSizeBytes = 100;
+
+const char kId[] = "blob-id";
+
+void BindBytesProvider(std::unique_ptr<MockBytesProvider> impl,
+                       mojom::BytesProviderRequest request) {
+  mojo::MakeStrongBinding(std::move(impl), std::move(request));
+}
+
+class BlobTransportStrategyTest : public testing::Test {
+ public:
+  void SetUp() override {
+    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+
+    bytes_provider_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+        {base::MayBlock(), base::WithBaseSyncPrimitives()});
+    mock_time_ = base::Time::Now();
+
+    limits_.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes;
+    limits_.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes;
+    limits_.max_bytes_data_item_size = kTestBlobStorageMaxBytesDataItemSize;
+    limits_.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize;
+    limits_.desired_max_disk_space = kTestBlobStorageMaxDiskSpace;
+    limits_.effective_max_disk_space = kTestBlobStorageMaxDiskSpace;
+    limits_.min_page_file_size = kTestBlobStorageMinFileSizeBytes;
+    limits_.max_file_size = kTestBlobStorageMaxFileSizeBytes;
+
+    mojo::edk::SetDefaultProcessErrorCallback(base::Bind(
+        &BlobTransportStrategyTest::OnBadMessage, base::Unretained(this)));
+
+    // Disallow IO on the main loop.
+    base::ThreadRestrictions::SetIOAllowed(false);
+  }
+
+  void TearDown() override {
+    base::ThreadRestrictions::SetIOAllowed(true);
+
+    mojo::edk::SetDefaultProcessErrorCallback(
+        mojo::edk::ProcessErrorCallback());
+  }
+
+  void OnBadMessage(const std::string& error) {
+    bad_messages_.push_back(error);
+  }
+
+  mojom::BytesProviderPtr CreateBytesProvider(const std::string& bytes,
+                                              base::Optional<base::Time> time) {
+    mojom::BytesProviderPtr result;
+    auto provider = base::MakeUnique<MockBytesProvider>(
+        std::vector<uint8_t>(bytes.begin(), bytes.end()), &reply_request_count_,
+        &stream_request_count_, &file_request_count_, time);
+    bytes_provider_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&BindBytesProvider, std::move(provider),
+                                  MakeRequest(&result)));
+    return result;
+  }
+
+ protected:
+  base::ScopedTempDir data_dir_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  scoped_refptr<base::SequencedTaskRunner> bytes_provider_runner_;
+  base::Time mock_time_;
+  storage::BlobStorageLimits limits_;
+
+  std::vector<std::string> bad_messages_;
+
+  size_t reply_request_count_ = 0;
+  size_t stream_request_count_ = 0;
+  size_t file_request_count_ = 0;
+};
+
+class BasicTests : public BlobTransportStrategyTest,
+                   public testing::WithParamInterface<MemoryStrategy> {};
+
+TEST_P(BasicTests, NoBytes) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      GetParam(), &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+  EXPECT_EQ(0u,
+            reply_request_count_ + stream_request_count_ + file_request_count_);
+  EXPECT_TRUE(bad_messages_.empty());
+}
+
+TEST_P(BasicTests, WithBytes) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      GetParam(), &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data = base::RandBytesAsString(7);
+  mojom::DataElementBytes bytes1(data.size(),
+                                 std::vector<uint8_t>(data.begin(), data.end()),
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes1);
+  expected.AppendData(data);
+
+  data = base::RandBytesAsString(3);
+  mojom::DataElementBytes bytes2(data.size(),
+                                 std::vector<uint8_t>(data.begin(), data.end()),
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes2);
+  expected.AppendData(data);
+
+  data = base::RandBytesAsString(10);
+  mojom::DataElementBytes bytes3(data.size(), base::nullopt,
+                                 CreateBytesProvider(data, mock_time_));
+  if (GetParam() != MemoryStrategy::NONE_NEEDED) {
+    strategy->AddBytesElement(&bytes3);
+    expected.AppendData(data);
+  }
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+  EXPECT_TRUE(bad_messages_.empty());
+
+  if (GetParam() == MemoryStrategy::NONE_NEEDED) {
+    EXPECT_EQ(
+        0u, reply_request_count_ + stream_request_count_ + file_request_count_);
+  } else {
+    EXPECT_EQ(
+        3u, reply_request_count_ + stream_request_count_ + file_request_count_);
+    switch (GetParam()) {
+      case MemoryStrategy::IPC:
+        EXPECT_EQ(3u, reply_request_count_);
+        break;
+      case MemoryStrategy::SHARED_MEMORY:
+        EXPECT_EQ(3u, stream_request_count_);
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(BlobTransportStrategyTest,
+                        BasicTests,
+                        testing::Values(MemoryStrategy::NONE_NEEDED,
+                                        MemoryStrategy::IPC,
+                                        MemoryStrategy::SHARED_MEMORY));
+
+class BasicErrorTests : public BlobTransportStrategyTest,
+                        public testing::WithParamInterface<MemoryStrategy> {};
+
+TEST_P(BasicErrorTests, NotEnoughBytesInProvider) {
+  BlobDataBuilder builder(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      GetParam(), &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data = base::RandBytesAsString(7);
+  mojom::DataElementBytes bytes(
+      data.size(), base::nullopt,
+      CreateBytesProvider(data.substr(0, 4), mock_time_));
+  strategy->AddBytesElement(&bytes);
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  EXPECT_TRUE(BlobStatusIsError(status));
+  EXPECT_EQ(GetParam() == MemoryStrategy::SHARED_MEMORY, bad_messages_.empty());
+}
+
+TEST_P(BasicErrorTests, TooManyBytesInProvider) {
+  BlobDataBuilder builder(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      GetParam(), &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data = base::RandBytesAsString(4);
+  mojom::DataElementBytes bytes(
+      data.size(), base::nullopt,
+      CreateBytesProvider(data + "foobar", mock_time_));
+  strategy->AddBytesElement(&bytes);
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  if (GetParam() == MemoryStrategy::SHARED_MEMORY) {
+    EXPECT_TRUE(status == BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS ||
+                status == BlobStatus::DONE);
+  } else {
+    EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status);
+    EXPECT_FALSE(bad_messages_.empty());
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(BlobTransportStrategyTest,
+                        BasicErrorTests,
+                        testing::Values(MemoryStrategy::IPC,
+                                        MemoryStrategy::SHARED_MEMORY));
+
+TEST_F(BlobTransportStrategyTest, DataStreamChunksData) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      MemoryStrategy::SHARED_MEMORY, &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data =
+      base::RandBytesAsString(kTestBlobStorageMaxSharedMemoryBytes * 3 + 13);
+  mojom::DataElementBytes bytes(data.size(), base::nullopt,
+                                CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes);
+
+  size_t offset = 0;
+  while (offset < data.size()) {
+    expected.AppendData(
+        data.substr(offset, kTestBlobStorageMaxBytesDataItemSize));
+    offset += kTestBlobStorageMaxBytesDataItemSize;
+  }
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(1u, stream_request_count_);
+  EXPECT_EQ(0u, file_request_count_);
+}
+
+TEST_F(BlobTransportStrategyTest, Files_NoBytes) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      MemoryStrategy::FILE, &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  strategy->BeginTransport(FileInfoVector());
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+  EXPECT_EQ(0u,
+            reply_request_count_ + stream_request_count_ + file_request_count_);
+  EXPECT_TRUE(bad_messages_.empty());
+}
+
+TEST_F(BlobTransportStrategyTest, Files_WriteFailed) {
+  BlobDataBuilder builder(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      MemoryStrategy::FILE, &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data = base::RandBytesAsString(kTestBlobStorageMaxFileSizeBytes);
+  mojom::DataElementBytes bytes(data.size(), base::nullopt,
+                                CreateBytesProvider(data, base::nullopt));
+  strategy->AddBytesElement(&bytes);
+
+  FileInfoVector files(1);
+  {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    base::FilePath path;
+    ASSERT_TRUE(base::CreateTemporaryFileInDir(data_dir_.GetPath(), &path));
+    files[0].file =
+        base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+    files[0].file_deletion_runner = base::ThreadTaskRunnerHandle::Get();
+    files[0].file_reference = ShareableFileReference::GetOrCreate(
+        path, ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+        bytes_provider_runner_.get());
+  }
+
+  strategy->BeginTransport(std::move(files));
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::ERR_FILE_WRITE_FAILED, status);
+}
+
+TEST_F(BlobTransportStrategyTest, Files_ValidBytesOneElement) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      MemoryStrategy::FILE, &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data =
+      base::RandBytesAsString(kTestBlobStorageMaxBlobMemorySize + 42);
+  mojom::DataElementBytes bytes(data.size(), base::nullopt,
+                                CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes);
+
+  size_t expected_file_count =
+      1 + data.size() / kTestBlobStorageMaxFileSizeBytes;
+  FileInfoVector files(expected_file_count);
+  for (size_t i = 0; i < expected_file_count; ++i) {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    base::FilePath path;
+    ASSERT_TRUE(base::CreateTemporaryFileInDir(data_dir_.GetPath(), &path));
+    files[i].file =
+        base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+    files[i].file_deletion_runner = base::ThreadTaskRunnerHandle::Get();
+    files[i].file_reference = ShareableFileReference::GetOrCreate(
+        path, ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+        bytes_provider_runner_.get());
+    size_t offset = i * kTestBlobStorageMaxFileSizeBytes;
+    size_t length = std::min<uint64_t>(kTestBlobStorageMaxFileSizeBytes,
+                                       data.size() - offset);
+    expected.AppendFile(path, 0, length, mock_time_);
+  }
+
+  strategy->BeginTransport(std::move(files));
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+  EXPECT_TRUE(bad_messages_.empty());
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(expected_file_count, file_request_count_);
+}
+
+TEST_F(BlobTransportStrategyTest, Files_ValidBytesMultipleElements) {
+  BlobDataBuilder builder(kId);
+  BlobDataBuilder expected(kId);
+
+  base::RunLoop loop;
+  BlobStatus status = BlobStatus::PENDING_TRANSPORT;
+  auto strategy = BlobTransportStrategy::Create(
+      MemoryStrategy::FILE, &builder,
+      base::BindOnce(
+          [](BlobStatus* result_out, base::OnceClosure closure,
+             BlobStatus result) {
+            *result_out = result;
+            std::move(closure).Run();
+          },
+          &status, loop.QuitClosure()),
+      limits_);
+
+  std::string data =
+      base::RandBytesAsString(kTestBlobStorageMaxBlobMemorySize / 3);
+
+  mojom::DataElementBytes bytes1(data.size(), base::nullopt,
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes1);
+  mojom::DataElementBytes bytes2(data.size(), base::nullopt,
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes2);
+  mojom::DataElementBytes bytes3(data.size(), base::nullopt,
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes3);
+  mojom::DataElementBytes bytes4(data.size(), base::nullopt,
+                                 CreateBytesProvider(data, mock_time_));
+  strategy->AddBytesElement(&bytes4);
+
+  size_t expected_file_count =
+      1 + 4 * data.size() / kTestBlobStorageMaxFileSizeBytes;
+  FileInfoVector files(expected_file_count);
+  for (size_t i = 0; i < expected_file_count; ++i) {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    base::FilePath path;
+    ASSERT_TRUE(base::CreateTemporaryFileInDir(data_dir_.GetPath(), &path));
+    files[i].file =
+        base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+    files[i].path = path;
+    files[i].file_deletion_runner = base::ThreadTaskRunnerHandle::Get();
+    files[i].file_reference = ShareableFileReference::GetOrCreate(
+        path, ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+        bytes_provider_runner_.get());
+  }
+
+  size_t file_offset = 0;
+  size_t file_index = 0;
+  size_t expected_request_count = 0;
+  for (size_t i = 0; i < 4; ++i) {
+    size_t remaining_size = data.size();
+    while (remaining_size > 0) {
+      size_t block_size = std::min<uint64_t>(
+          kTestBlobStorageMaxFileSizeBytes - file_offset, remaining_size);
+      expected.AppendFile(files[file_index].path, file_offset, block_size,
+                          mock_time_);
+      expected_request_count++;
+      remaining_size -= block_size;
+      file_offset += block_size;
+      if (file_offset >= kTestBlobStorageMaxFileSizeBytes) {
+        file_offset = 0;
+        file_index++;
+      }
+    }
+  }
+
+  strategy->BeginTransport(std::move(files));
+  loop.Run();
+
+  EXPECT_EQ(BlobStatus::DONE, status);
+  EXPECT_EQ(expected, builder);
+  EXPECT_TRUE(bad_messages_.empty());
+  EXPECT_EQ(0u, reply_request_count_);
+  EXPECT_EQ(0u, stream_request_count_);
+  EXPECT_EQ(expected_request_count, file_request_count_);
+}
+
+}  // namespace
+
+}  // namespace storage
diff --git a/storage/browser/test/mock_bytes_provider.cc b/storage/browser/test/mock_bytes_provider.cc
new file mode 100644
index 0000000..80de185
--- /dev/null
+++ b/storage/browser/test/mock_bytes_provider.cc
@@ -0,0 +1,58 @@
+// 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 "storage/browser/test/mock_bytes_provider.h"
+
+#include "mojo/common/data_pipe_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace storage {
+
+MockBytesProvider::MockBytesProvider(
+    std::vector<uint8_t> data,
+    size_t* reply_request_count,
+    size_t* stream_request_count,
+    size_t* file_request_count,
+    base::Optional<base::Time> file_modification_time)
+    : data_(std::move(data)),
+      reply_request_count_(reply_request_count),
+      stream_request_count_(stream_request_count),
+      file_request_count_(file_request_count),
+      file_modification_time_(file_modification_time) {}
+
+MockBytesProvider::~MockBytesProvider() {}
+
+void MockBytesProvider::RequestAsReply(RequestAsReplyCallback callback) {
+  if (reply_request_count_)
+    ++*reply_request_count_;
+  std::move(callback).Run(data_);
+}
+
+void MockBytesProvider::RequestAsStream(
+    mojo::ScopedDataPipeProducerHandle pipe) {
+  if (stream_request_count_)
+    ++*stream_request_count_;
+  mojo::common::BlockingCopyFromString(
+      std::string(reinterpret_cast<const char*>(data_.data()), data_.size()),
+      pipe);
+}
+
+void MockBytesProvider::RequestAsFile(uint64_t source_offset,
+                                      uint64_t source_size,
+                                      base::File file,
+                                      uint64_t file_offset,
+                                      RequestAsFileCallback callback) {
+  if (file_request_count_)
+    ++*file_request_count_;
+  EXPECT_LE(source_offset + source_size, data_.size());
+  EXPECT_EQ(source_size,
+            static_cast<uint64_t>(file.Write(
+                file_offset,
+                reinterpret_cast<const char*>(data_.data() + source_offset),
+                source_size)));
+  EXPECT_TRUE(file.Flush());
+  std::move(callback).Run(file_modification_time_);
+}
+
+}  // namespace storage
diff --git a/storage/browser/test/mock_bytes_provider.h b/storage/browser/test/mock_bytes_provider.h
new file mode 100644
index 0000000..ef16b63
--- /dev/null
+++ b/storage/browser/test/mock_bytes_provider.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
+#define STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
+
+#include "storage/public/interfaces/blobs.mojom.h"
+
+namespace storage {
+
+// Mock BytesProvider implementation. RequestAsStream blocks, so make sure to
+// bind this implementation to a pipe on a separate sequence from where the
+// bytes are consumed.
+class MockBytesProvider : public mojom::BytesProvider {
+ public:
+  explicit MockBytesProvider(
+      std::vector<uint8_t> data,
+      size_t* reply_request_count = nullptr,
+      size_t* stream_request_count = nullptr,
+      size_t* file_request_count = nullptr,
+      base::Optional<base::Time> file_modification_time = base::Time());
+  ~MockBytesProvider() override;
+
+  // BytesProvider implementation:
+  void RequestAsReply(RequestAsReplyCallback callback) override;
+  void RequestAsStream(mojo::ScopedDataPipeProducerHandle pipe) override;
+  void RequestAsFile(uint64_t source_offset,
+                     uint64_t source_size,
+                     base::File file,
+                     uint64_t file_offset,
+                     RequestAsFileCallback callback) override;
+
+ private:
+  std::vector<uint8_t> data_;
+  size_t* reply_request_count_;
+  size_t* stream_request_count_;
+  size_t* file_request_count_;
+  base::Optional<base::Time> file_modification_time_;
+};
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
diff --git a/storage/common/blob_storage/blob_storage_constants.cc b/storage/common/blob_storage/blob_storage_constants.cc
index a877255f..9269115f 100644
--- a/storage/common/blob_storage/blob_storage_constants.cc
+++ b/storage/common/blob_storage/blob_storage_constants.cc
@@ -17,6 +17,7 @@
 
 bool BlobStorageLimits::IsValid() const {
   return max_ipc_memory_size < max_shared_memory_size &&
+         max_ipc_memory_size < max_bytes_data_item_size &&
          min_page_file_size < max_file_size &&
          min_page_file_size < max_blob_in_memory_space &&
          effective_max_disk_space <= desired_max_disk_space;
diff --git a/storage/common/blob_storage/blob_storage_constants.h b/storage/common/blob_storage/blob_storage_constants.h
index 788a063..7450943 100644
--- a/storage/common/blob_storage/blob_storage_constants.h
+++ b/storage/common/blob_storage/blob_storage_constants.h
@@ -52,6 +52,12 @@
   // This is the maximum size of a shared memory handle.
   size_t max_shared_memory_size = kDefaultSharedMemorySize;
 
+  // This is the maximum size of a bytes BlobDataItem. Only used for mojo
+  // based blob transportation, as the old IPC/shared memory based
+  // implementation doesn't support different values for this and
+  // max_shared_memory_size.
+  size_t max_bytes_data_item_size = kDefaultSharedMemorySize;
+
   // This is the maximum amount of memory we can use to store blobs.
   size_t max_blob_in_memory_space = kDefaultMaxBlobInMemorySpace;
   // The ratio applied to |max_blob_in_memory_space| to reduce memory usage
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6070e109..6580512c 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -234,783 +234,6 @@
       }
     ]
   },
-  "CFI Linux": {
-    "gtest_tests": [
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "device_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "display_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "events_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gn_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "url_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "views_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
-  "CFI Linux Full": {
-    "gtest_tests": [
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "device_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "display_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "events_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--no-xvfb"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:104a",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gl_unittests",
-        "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gn_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "url_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "views_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
   "CFI Linux ToT": {
     "gtest_tests": [
       {
@@ -1388,368 +611,6 @@
       }
     ]
   },
-  "CFI ThinLTO Linux ToT": {
-    "gtest_tests": [
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-SaveType/SavePageMultiFrameBrowserTest.ObjectElements/0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "device_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "display_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "events_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gn_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "url_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "views_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
   "Chromium Mac 10.10 MacViews": {
     "gtest_tests": [
       {
@@ -11239,374 +10100,6 @@
       }
     ]
   },
-  "LTO Linux": {
-    "gtest_tests": [
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-SaveType/SavePageMultiFrameBrowserTest.ObjectElements/0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "device_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "display_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "events_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gn_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "url_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "views_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
   "Linux ARM": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 4fcfca8..4da910c1 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -4411,7 +4411,8 @@
           "-v",
           "--upload-results",
           "--output-format=chartjson",
-          "--browser=release_x64"
+          "--browser=release_x64",
+          "--output-format=json-test-results"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -4441,6 +4442,7 @@
           "--upload-results",
           "--output-format=chartjson",
           "--browser=reference",
+          "--output-format=json-test-results",
           "--output-trace-tag=_ref"
         ],
         "isolate_name": "telemetry_perf_tests",
@@ -4470,7 +4472,8 @@
           "-v",
           "--upload-results",
           "--output-format=chartjson",
-          "--browser=release_x64"
+          "--browser=release_x64",
+          "--output-format=json-test-results"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -4500,6 +4503,7 @@
           "--upload-results",
           "--output-format=chartjson",
           "--browser=reference",
+          "--output-format=json-test-results",
           "--output-trace-tag=_ref"
         ],
         "isolate_name": "telemetry_perf_tests",
@@ -9479,7 +9483,8 @@
           "-v",
           "--upload-results",
           "--output-format=chartjson",
-          "--browser=release_x64"
+          "--browser=release_x64",
+          "--output-format=json-test-results"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -9509,6 +9514,7 @@
           "--upload-results",
           "--output-format=chartjson",
           "--browser=reference",
+          "--output-format=json-test-results",
           "--output-trace-tag=_ref"
         ],
         "isolate_name": "telemetry_perf_tests",
@@ -9538,7 +9544,8 @@
           "-v",
           "--upload-results",
           "--output-format=chartjson",
-          "--browser=release_x64"
+          "--browser=release_x64",
+          "--output-format=json-test-results"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -9568,6 +9575,7 @@
           "--upload-results",
           "--output-format=chartjson",
           "--browser=reference",
+          "--output-format=json-test-results",
           "--output-trace-tag=_ref"
         ],
         "isolate_name": "telemetry_perf_tests",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2a157a0d..6bea4f56 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2780,7 +2780,7 @@
                 {
                     "name": "AdIdentifiers",
                     "params": {
-                        "tag_attribute_csv": "div,id,iframe,id"
+                        "tag_attribute_csv": "div,data-google-query-id,div,id,iframe,id"
                     },
                     "enable_features": [
                         "ThreatDomDetailsTagAttributes"
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index b28717d..a72431f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -7363,7 +7363,6 @@
 crbug.com/591099 fast/encoding/bracket-in-script.html [ Failure ]
 crbug.com/591099 fast/encoding/charset-invalid.html [ Failure ]
 crbug.com/591099 fast/encoding/charset-koi8-u.html [ Failure ]
-crbug.com/591099 fast/encoding/charset-replacement.html [ Failure ]
 crbug.com/591099 fast/encoding/charset-xuser-defined.html [ Crash Failure ]
 crbug.com/591099 fast/encoding/css-charset-default.xhtml [ Failure ]
 crbug.com/591099 fast/encoding/css-charset.html [ Failure ]
@@ -13669,13 +13668,13 @@
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Crash Failure Timeout ]
 crbug.com/591099 inspector-protocol/emulation/device-emulation-small-dw.js [ Failure ]
 crbug.com/591099 inspector-protocol/emulation/device-emulation-small.js [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/heap-objects-tracking.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/heap-samples-in-snapshot.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/sampling-heap-profiler.html [ Failure ]
-crbug.com/591099 inspector-protocol/heap-profiler/take-heap-snapshot.html [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/heap-objects-tracking.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/sampling-heap-profiler.js [ Failure ]
+crbug.com/591099 inspector-protocol/heap-profiler/take-heap-snapshot.js [ Failure ]
 crbug.com/591099 inspector-protocol/layers/paint-profiler.js [ Failure ]
 crbug.com/591099 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.html [ Failure ]
 crbug.com/591099 inspector/agents-enable-disable.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index a8cc4d4..54d1719a 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -1408,10 +1408,10 @@
 Bug(none) inspector-protocol/css/css-get-background-colors.html [ Timeout ]
 Bug(none) inspector-protocol/css/cssom-modify-rule-and-get-rule-list.html [ Timeout ]
 Bug(none) inspector-protocol/dom/dom-request-document-with-child-nodes.js [ Failure ]
-Bug(none) inspector-protocol/heap-profiler/heap-samples-in-snapshot.html [ Failure ]
-Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html [ Failure ]
-Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html [ Failure ]
-Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html [ Failure ]
+Bug(none) inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Failure ]
+Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js [ Failure ]
+Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js [ Failure ]
+Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js [ Failure ]
 Bug(none) inspector-protocol/layout-fonts/unicode-range-combining-chars-fallback.html [ Failure ]
 Bug(none) inspector-protocol/network/resource-type.js [ Timeout ]
 Bug(none) inspector-protocol/network/websocket-initiator.js [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/MSANExpectations b/third_party/WebKit/LayoutTests/MSANExpectations
index e7a990da..cf7d6fef 100644
--- a/third_party/WebKit/LayoutTests/MSANExpectations
+++ b/third_party/WebKit/LayoutTests/MSANExpectations
@@ -32,10 +32,10 @@
 crbug.com/704360 [ Linux ] shapedetection/detection-ImageData.html [ Timeout Pass ]
 
 # Times out on MSAN
-crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-samples-in-snapshot.html [ Timeout ]
-crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html [ Timeout ]
-crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html [ Timeout ]
-crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html [ Timeout ]
+crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Timeout ]
+crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js [ Timeout ]
+crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js [ Timeout ]
+crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js [ Timeout ]
 
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag-replace-state.html [ Timeout Pass ]
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 8ce7227..0f316d7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1429,6 +1429,8 @@
 crbug.com/736319 [ Linux Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-003.html [ Failure Pass ]
 crbug.com/736319 [ Linux Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-004.html [ Failure Pass ]
 crbug.com/736319 [ Linux Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-005.html [ Failure Pass ]
+crbug.com/736319 [ Linux Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-006a.html [ Failure Pass ]
+crbug.com/736319 [ Linux Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-007.html [ Failure Pass ]
 
 # These need a rebaseline due crbug.com/504745 on Windows when they are activated again.
 crbug.com/521124 crbug.com/410145 [ Win7 ] fast/css/font-weight-1.html [ Pass Failure ]
@@ -2542,6 +2544,251 @@
 
 # ====== Random order flaky tests end here ======
 
+# ====== Tests from enabling .any.js/.worker.js tests begin here ======
+crbug.com/709227 external/wpt/FileAPI/idlharness.worker.html [ Failure ]
+crbug.com/709227 external/wpt/IndexedDB/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/digest/digest.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/encrypt_decrypt/rsa.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_AES-CBC.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_AES-CTR.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_AES-GCM.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_AES-KW.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_ECDH.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_ECDSA.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_HMAC.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_RSA-OAEP.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_RSA-PSS.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_AES-CBC.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_AES-GCM.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_AES-KW.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_ECDH.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_ECDSA.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_HMAC.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_RSA-PSS.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/idlharness.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/import_export/ec_importKey.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/import_export/rsa_importKey.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/import_export/symmetric_importKey.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/sign_verify/ecdsa.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/sign_verify/hmac.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/sign_verify/rsa_pss.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.worker.html [ Failure ]
+crbug.com/709227 external/wpt/WebIDL/ecmascript-binding/es-exceptions/constructor-object.worker.html [ Failure ]
+crbug.com/709227 external/wpt/background-fetch/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-count-label-conversion.any.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-count-label-conversion.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-time-label-conversion.any.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-time-label-conversion.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-timeline-timelineEnd-historical.any.html [ Failure ]
+crbug.com/709227 external/wpt/console/console-timeline-timelineEnd-historical.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/css/geometry-1/DOMMatrix-css-string.worker.html [ Failure ]
+crbug.com/709227 external/wpt/css/geometry-1/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/basic/request-upload.any.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/basic/request-upload.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/basic/scheme-about.any.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/basic/scheme-about.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-redirect.any.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-redirect.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-referrer.any.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-referrer.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-star.any.html [ Failure ]
+crbug.com/709227 external/wpt/fetch/api/cors/cors-preflight-star.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/html/browsers/history/the-location-interface/per-global.window.html [ Failure ]
+crbug.com/709227 external/wpt/html/dom/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_isindex.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_isindex.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_isindex.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_menuitem-element.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_menuitem-element.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_menuitem-element.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_template.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_template.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_template.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests11.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests11.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests11.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests19.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests19.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests19.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests2.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests2.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests2.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests25.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests25.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_tests25.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_webkit02.html?run_type=uri [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_webkit02.html?run_type=write [ Failure ]
+crbug.com/709227 external/wpt/html/syntax/parsing/html5lib_webkit02.html?run_type=write_single [ Failure ]
+crbug.com/709227 external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.html [ Failure ]
+crbug.com/709227 external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.outside3.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.arc.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.closed.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.curve.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.line.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.rect.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/pixel-manipulation/2d.imageData.create2.nonfinite.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/pixel-manipulation/2d.imageData.get.nonfinite.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/pixel-manipulation/2d.imageData.get.tiny.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/pixel-manipulation/2d.imageData.put.nonfinite.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/2d.getcontext.extraargs.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/offscreencanvas.getcontext.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.idl.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.em.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.empty.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.junk.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.minus.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.onlyspace.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.percent.worker.html [ Failure ]
+crbug.com/709227 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.trailingjunk.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-disconnect.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-getentries.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-mark-measure.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-observe.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/storage/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/url/historical.any.html [ Failure ]
+crbug.com/709227 external/wpt/url/historical.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/url/interfaces.any.html [ Failure ]
+crbug.com/709227 external/wpt/url/interfaces.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/url/toascii.window.html [ Failure ]
+crbug.com/709227 external/wpt/user-timing/invoke_with_timing_attributes.worker.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/closing-handshake/002.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/closing-handshake/003.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/closing-handshake/004.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/002.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/002.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/006.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/009.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/010.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/010.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/011.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/011.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/013.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/014.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/016.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/018.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/019.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/020.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/constructor/022.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/001.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/002.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/003.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/004.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/005.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/005.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/006.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/007.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/cookies/007.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/CloseEvent/clean-close.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-getting.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/close/close-connecting.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/close/close-connecting.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/close/close-nested.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/close/close-nested.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/events/016.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/readyState/003.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/readyState/003.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/readyState/006.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/readyState/007.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/readyState/008.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/007.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/008.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/009.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/011.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/012.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/keeping-connection-open/001.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/opening-handshake/002.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/opening-handshake/003.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/opening-handshake/005.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/unload-a-document/001.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/unload-a-document/001.html?wss [ Failure ]
+crbug.com/709227 external/wpt/websockets/unload-a-document/002.html [ Failure ]
+crbug.com/709227 external/wpt/websockets/unload-a-document/002.html?wss [ Failure ]
+crbug.com/709227 external/wpt/workers/constructors/Worker/DedicatedWorkerGlobalScope-members.worker.html [ Failure ]
+crbug.com/709227 external/wpt/workers/constructors/Worker/expected-self-properties.worker.html [ Failure ]
+crbug.com/709227 external/wpt/workers/interfaces.worker.html [ Failure ]
+crbug.com/709227 external/wpt/workers/interfaces/WorkerUtils/importScripts/002.worker.html [ Failure ]
+crbug.com/709227 external/wpt/workers/semantics/interface-objects/001.worker.html [ Failure ]
+crbug.com/709227 external/wpt/workers/semantics/interface-objects/002.worker.html [ Failure ]
+crbug.com/709227 http/tests/inspector-protocol/network/disable-interception-midway.html [ Failure ]
+crbug.com/709227 external/wpt/WebIDL/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.html [ Failure ]
+crbug.com/709227 external/wpt/WebIDL/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/dom/abort/event.any.html [ Failure ]
+crbug.com/709227 external/wpt/dom/abort/event.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/dom/events/EventTarget-constructible.any.html [ Failure ]
+crbug.com/709227 external/wpt/dom/events/EventTarget-constructible.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-callback-mutate.any.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-callback-mutate.any.worker.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-disconnect.any.html [ Failure ]
+crbug.com/709227 external/wpt/performance-timeline/po-entries-sort.any.worker.html [ Failure ]
+
+# Timeouts
+crbug.com/709227 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.worker.html [ Timeout ]
+crbug.com/709227 external/wpt/websockets/binary/001.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/binary/002.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/binary/004.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/binary/005.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/extended-payload-length.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-arraybuffer.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-blob.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-large.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-unicode.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/events/018.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/005.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/006.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/interfaces/WebSocket/send/010.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/websockets/opening-handshake/003-sets-origin.worker.html [ Timeout ]
+crbug.com/709227 external/wpt/websockets/opening-handshake/005.html?wss [ Timeout ]
+crbug.com/709227 external/wpt/workers/nested_worker.worker.html [ Timeout ]
+
+# Crashes
+crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.html [ Crash ]
+
+# ====== Tests from enabling .any.js/.worker.js tests end here ========
+
 # ====== Begin of display: contents tests ======
 
 crbug.com/657748 external/wpt/css/css-display-3/display-contents-before-after-002.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/char-decoding.html b/third_party/WebKit/LayoutTests/fast/encoding/char-decoding.html
index 9d7b4466..2ee9cd4 100644
--- a/third_party/WebKit/LayoutTests/fast/encoding/char-decoding.html
+++ b/third_party/WebKit/LayoutTests/fast/encoding/char-decoding.html
@@ -144,7 +144,7 @@
 testDecode('unicodeFFFE', '%D8%69%DE%D6', 'U+D869/U+DED6');
 
 // Replacement encodings should decode non-empty streams as replacement (U+FFFD) then EOF
-["csiso2022kr", "hz-gb-2312", "iso-2022-cn", "iso-2022-cn-ext", "iso-2022-kr"]
+["replacement", "csiso2022kr", "hz-gb-2312", "iso-2022-cn", "iso-2022-cn-ext", "iso-2022-kr"]
 .forEach(function(encoding) {
     testDecode(encoding, "", "");
     testDecode(encoding, "%41%42%43%61%62%63%31%32%33%A0", "U+FFFD");
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement-expected.txt b/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement-expected.txt
deleted file mode 100644
index 4009bfa33..0000000
--- a/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Should be your browser default encoding: windows-1252
-
-If it's latin-1 (ISO-8859-1, windows-1252), this should be accented e: é
-
-If you got a render tree with U+FFFD instead, this failed
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement.html b/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement.html
deleted file mode 100644
index 1e046fd..0000000
--- a/third_party/WebKit/LayoutTests/fast/encoding/charset-replacement.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<meta http-equiv="content-type" content="text/html; charset=replacement">
-<title>Replacement encoding - spec/implementation detail, not a real encoding</title>
-<script>
-if (window.testRunner)
-    testRunner.dumpAsText();
-document.write("<p>Should be your browser default encoding: " + document.characterSet + "</p>");
-document.write("<p>If it's latin-1 (ISO-8859-1, windows-1252), this should be accented e: é</p>");
-document.write("<p>If you got a render tree with U+FFFD instead, this failed</p>");
-</script>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking-expected.txt
index 96e5fc7..4316c11c 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking-expected.txt
@@ -1,5 +1,4 @@
 Test that heap tracking actually reports data fragments.
-
 SUCCESS: tracking started
 HeapProfiler.heapStatsUpdate has params: true
 HeapProfiler.heapStatsUpdate has statsUpdate: true
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.html
deleted file mode 100644
index 0c6cd2fb..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.html
+++ /dev/null
@@ -1,86 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function junkGenerator()
-{
-    var junkArray = new Array(1000);
-    for (var i = 0; i < junkArray.length; ++i)
-        junkArray[i] = "42 " + i;
-    window.junkArray = junkArray;
-}
-
-function setupIntervalAndRunTest()
-{
-    setInterval(junkGenerator, 0);
-    runTest();
-}
-
-function test()
-{
-    var lastSeenObjectIdEventCount = 0;
-    var heapStatsUpdateEventCount = 0;
-    function trackingStarted()
-    {
-        InspectorTest.log("SUCCESS: tracking started");
-    }
-
-    function trackingStopped()
-    {
-        InspectorTest.log("Number of heapStatsUpdate events >= numbrt of lastSeenObjectId events: " + (heapStatsUpdateEventCount >= lastSeenObjectIdEventCount));
-        InspectorTest.log("At least 2 lastSeenObjectId arrived: " + (lastSeenObjectIdEventCount >= 2));
-        InspectorTest.log("SUCCESS: tracking stopped");
-        InspectorTest.completeTest();
-    }
-
-    var fragments = [];
-    InspectorTest.eventHandler["HeapProfiler.lastSeenObjectId"] = function(messageObject)
-    {
-        if (++lastSeenObjectIdEventCount <= 2) {
-            var params = messageObject["params"];
-            InspectorTest.log("HeapProfiler.lastSeenObjectId has params: " + !!params);
-            InspectorTest.log("HeapProfiler.lastSeenObjectId has params.lastSeenObjectId: " + !!params.lastSeenObjectId);
-            InspectorTest.log("HeapProfiler.lastSeenObjectId has timestamp: " + !!params.timestamp);
-            InspectorTest.log("A heap stats fragment did arrive before HeapProfiler.lastSeenObjectId: " + !!fragments.length);
-            InspectorTest.log("");
-        }
-        if (lastSeenObjectIdEventCount == 2) {
-            // Wait for two updates and then stop tracing.
-             InspectorTest.sendCommand("HeapProfiler.stopTrackingHeapObjects", {}, trackingStopped);
-        }
-    }
-
-    InspectorTest.eventHandler["HeapProfiler.heapStatsUpdate"] = function(messageObject)
-    {
-        ++heapStatsUpdateEventCount
-        var params = messageObject["params"];
-        if (heapStatsUpdateEventCount <= 2)
-            InspectorTest.log("HeapProfiler.heapStatsUpdate has params: " + !!params);
-        var statsUpdate = params.statsUpdate;
-        if (heapStatsUpdateEventCount <= 2) {
-            InspectorTest.log("HeapProfiler.heapStatsUpdate has statsUpdate: " + !!statsUpdate);
-            InspectorTest.log("statsUpdate length is not zero: " + !!statsUpdate.length);
-            InspectorTest.log("statsUpdate length is a multiple of three: " + !(statsUpdate.length % 3));
-            InspectorTest.log("statsUpdate: first fragmentIndex in first update: " + statsUpdate[0]);
-            InspectorTest.log("statsUpdate: total count of objects is not zero: " + !!statsUpdate[1]);
-            InspectorTest.log("statsUpdate: total size of objects is not zero: " + !!statsUpdate[2]);
-            InspectorTest.log("");
-        }
-        fragments.push(statsUpdate);
-    }
-
-    InspectorTest.sendCommand("HeapProfiler.startTrackingHeapObjects", {}, trackingStarted);
-    //# sourceURL=heap-objects-tracking.html
-}
-</script>
-</head>
-<body onload="setupIntervalAndRunTest()">
-<p>Test that heap tracking actually reports data fragments.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.js
new file mode 100644
index 0000000..b312cc0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-objects-tracking.js
@@ -0,0 +1,58 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Test that heap tracking actually reports data fragments.');
+
+  await session.evaluate(`
+    var junkArray = new Array(1000);
+    function junkGenerator() {
+      for (var i = 0; i < junkArray.length; ++i)
+        junkArray[i] = '42 ' + i;
+      window.junkArray = junkArray;
+    }
+    setInterval(junkGenerator, 0)
+  `);
+
+  var lastSeenObjectIdEventCount = 0;
+  var heapStatsUpdateEventCount = 0;
+  var fragments = [];
+
+  dp.HeapProfiler.onLastSeenObjectId(async messageObject => {
+    ++lastSeenObjectIdEventCount;
+    if (lastSeenObjectIdEventCount <= 2) {
+      var params = messageObject['params'];
+      testRunner.log('HeapProfiler.lastSeenObjectId has params: ' + !!params);
+      testRunner.log('HeapProfiler.lastSeenObjectId has params.lastSeenObjectId: ' + !!params.lastSeenObjectId);
+      testRunner.log('HeapProfiler.lastSeenObjectId has timestamp: ' + !!params.timestamp);
+      testRunner.log('A heap stats fragment did arrive before HeapProfiler.lastSeenObjectId: ' + !!fragments.length);
+      testRunner.log('');
+    }
+    if (lastSeenObjectIdEventCount === 2) {
+      // Wait for two updates and then stop tracing.
+      await dp.HeapProfiler.stopTrackingHeapObjects();
+      testRunner.log('Number of heapStatsUpdate events >= numbrt of lastSeenObjectId events: ' + (heapStatsUpdateEventCount >= lastSeenObjectIdEventCount));
+      testRunner.log('At least 2 lastSeenObjectId arrived: ' + (lastSeenObjectIdEventCount >= 2));
+      testRunner.log('SUCCESS: tracking stopped');
+      testRunner.completeTest();
+    }
+  });
+
+  dp.HeapProfiler.onHeapStatsUpdate(messageObject => {
+    ++heapStatsUpdateEventCount;
+    var params = messageObject['params'];
+    if (heapStatsUpdateEventCount <= 2)
+      testRunner.log('HeapProfiler.heapStatsUpdate has params: ' + !!params);
+    var statsUpdate = params.statsUpdate;
+    if (heapStatsUpdateEventCount <= 2) {
+      testRunner.log('HeapProfiler.heapStatsUpdate has statsUpdate: ' + !!statsUpdate);
+      testRunner.log('statsUpdate length is not zero: ' + !!statsUpdate.length);
+      testRunner.log('statsUpdate length is a multiple of three: ' + !(statsUpdate.length % 3));
+      testRunner.log('statsUpdate: first fragmentIndex in first update: ' + statsUpdate[0]);
+      testRunner.log('statsUpdate: total count of objects is not zero: ' + !!statsUpdate[1]);
+      testRunner.log('statsUpdate: total size of objects is not zero: ' + !!statsUpdate[2]);
+      testRunner.log('');
+    }
+    fragments.push(statsUpdate);
+  });
+
+  await dp.HeapProfiler.startTrackingHeapObjects();
+  testRunner.log('SUCCESS: tracking started');
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot-expected.txt
index 85c68bc9..749f71c 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot-expected.txt
@@ -1,5 +1,4 @@
 Test that heap tracking actually reports data fragments.
-
 Tracking started
 Took heap snapshot
 Parsed snapshot
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.html
deleted file mode 100644
index a23b84e..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.html
+++ /dev/null
@@ -1,95 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function junkGenerator()
-{
-    var junkArray = new Array(1000);
-    for (var i = 0; i < junkArray.length; ++i)
-        junkArray[i] = "42 " + i;
-    window.junkArray = junkArray;
-}
-
-function setupIntervalAndRunTest()
-{
-    setInterval(junkGenerator, 0);
-    runTest();
-}
-
-function test()
-{
-    InspectorTest.importScript("../../../../inspector-protocol/heap-profiler/resources/heap-snapshot-common.js");
-
-    function trackingStarted()
-    {
-        InspectorTest.log("Tracking started");
-    }
-
-    function arraysEqual(a, b)
-    {
-        var sa = a.join(", ");
-        var sb = b.join(", ");
-        if (sa === sb)
-            return true;
-        InspectorTest.log("FAIL:\na = [" + sa+ "]\nb = [" + sb + "]");
-        return false;
-    }
-
-    function trackingStopped(snapshot)
-    {
-        var samples = snapshot.getSamples();
-        InspectorTest.log("Last assigned id arrays match: " + arraysEqual(lastAssignedIds, samples.lastAssignedIds));
-        var sizesMatch = (sizes.length <= samples.sizes.length);
-        InspectorTest.log("Size arrays length is correct: " + sizesMatch);
-        if (!sizesMatch) {
-            // Print mismatch:
-            arraysEqual(sizes, samples.sizes);
-        }
-        var sizesNonGrowing = true;
-        for (var i = 0; i < samples.sizes.length; i++) {
-            if ((sizes[i] === undefined && samples.sizes[i] !== 0) || (sizes[i] < samples.sizes[i])) {
-                sizesNonGrowing = false;
-                InspectorTest.log("FAIL: total size of live objects from interval cannot increase.");
-                // Print mismatch:
-                arraysEqual(sizes, samples.sizes);
-            }
-        }
-        InspectorTest.log("Sizes non growing: " + sizesNonGrowing);
-        InspectorTest.completeTest();
-    }
-
-    var sizes = [];
-    var lastAssignedIds = [];
-    InspectorTest.eventHandler["HeapProfiler.lastSeenObjectId"] = function(messageObject)
-    {
-        lastAssignedIds.push(messageObject["params"]["lastSeenObjectId"]);
-        if (lastAssignedIds.length === 2) {
-            // Wait for two updates and then stop tracing.
-            InspectorTest.stopRecordingHeapTimeline(trackingStopped);
-        }
-    }
-
-    InspectorTest.eventHandler["HeapProfiler.heapStatsUpdate"] = function(messageObject)
-    {
-        var samples = messageObject["params"]["statsUpdate"];
-        for (var i = 0; i < samples.length; i += 3) {
-            index = samples[i];
-            sizes[index] = samples[i+2];
-        }
-    }
-
-    InspectorTest.sendCommand("HeapProfiler.startTrackingHeapObjects", {}, trackingStarted);
-    //# sourceURL=heap-objects-tracking.html
-}
-</script>
-</head>
-<body onload="setupIntervalAndRunTest()">
-<p>Test that heap tracking actually reports data fragments.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.js
new file mode 100644
index 0000000..3439530
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-samples-in-snapshot.js
@@ -0,0 +1,65 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Test that heap tracking actually reports data fragments.');
+
+  await session.evaluate(`
+    var junkArray = new Array(1000);
+    function junkGenerator() {
+      for (var i = 0; i < junkArray.length; ++i)
+        junkArray[i] = '42 ' + i;
+      window.junkArray = junkArray;
+    }
+    setInterval(junkGenerator, 0)
+  `);
+
+  var Helper = await testRunner.loadScript('resources/heap-snapshot-common.js');
+  var helper = await Helper(testRunner, session);
+
+  function arraysEqual(a, b) {
+    var sa = a.join(', ');
+    var sb = b.join(', ');
+    if (sa === sb)
+      return true;
+    testRunner.log('FAIL:\na = [' + sa + ']\nb = [' + sb + ']');
+    return false;
+  }
+
+  var sizes = [];
+  var lastAssignedIds = [];
+  dp.HeapProfiler.onLastSeenObjectId(async messageObject => {
+    lastAssignedIds.push(messageObject['params']['lastSeenObjectId']);
+    if (lastAssignedIds.length === 2) {
+      // Wait for two updates and then stop tracing.
+      var snapshot = await helper.stopRecordingHeapTimeline();
+      var samples = snapshot.getSamples();
+      testRunner.log('Last assigned id arrays match: ' + arraysEqual(lastAssignedIds, samples.lastAssignedIds));
+      var sizesMatch = (sizes.length <= samples.sizes.length);
+      testRunner.log('Size arrays length is correct: ' + sizesMatch);
+      if (!sizesMatch) {
+        // Print mismatch:
+        arraysEqual(sizes, samples.sizes);
+      }
+      var sizesNonGrowing = true;
+      for (var i = 0; i < samples.sizes.length; i++) {
+        if ((sizes[i] === undefined && samples.sizes[i] !== 0) || (sizes[i] < samples.sizes[i])) {
+          sizesNonGrowing = false;
+          testRunner.log('FAIL: total size of live objects from interval cannot increase.');
+          // Print mismatch:
+          arraysEqual(sizes, samples.sizes);
+        }
+      }
+      testRunner.log('Sizes non growing: ' + sizesNonGrowing);
+      testRunner.completeTest();
+    }
+  });
+
+  dp.HeapProfiler.onHeapStatsUpdate(messageObject => {
+    var samples = messageObject['params']['statsUpdate'];
+    for (var i = 0; i < samples.length; i += 3) {
+      var index = samples[i];
+      sizes[index] = samples[i+2];
+    }
+  });
+
+  await dp.HeapProfiler.startTrackingHeapObjects();
+  testRunner.log('Tracking started');
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object-expected.txt
index 7157aa1..bb87c1d1 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object-expected.txt
@@ -1,7 +1,5 @@
-CONSOLE MESSAGE: line 21: Created 2 MediaQueryList eleements
 Test that all ActiveDOMObjects with pending activities will get into one group in the heap snapshot. Bug 426809.
-
- Took heap snapshot
+Took heap snapshot
 Parsed snapshot
 SUCCESS: found (Pending activities group)
 SUCCESS: found Pending activities / 3 entries
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html
deleted file mode 100644
index 1b0c19c..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function onChange(e)
-{
-    console.log("onChange " + e.matches);
-}
-
-function createTwoActiveDOMObjectsAndRunTest()
-{
-    var m = window.matchMedia("(min-width: 1400px)");
-    m.addListener(onChange);
-    m = window.matchMedia("(min-height: 1800px)");
-    m.addListener(onChange);
-    console.log("Created 2 MediaQueryList eleements");
-    runTest();
-}
-
-function test()
-{
-    InspectorTest.importScript("../../../../inspector-protocol/heap-profiler/resources/heap-snapshot-common.js");
-
-    function checkHeapSnapshot(snapshot) {
-        var node;
-        for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
-            if (it.node.name() === "(Pending activities group)") {
-                node = it.node;
-                break;
-            }
-        }
-        if (node)
-            InspectorTest.log("SUCCESS: found " + node.name());
-        else
-            return InspectorTest.fail("cannot find '(Pending activities group)'");
-
-        var pendingActivitiesRE = /^Pending activities/;
-        var pendingActivitiesFound = false;
-        for (var iter = node.edges(); iter.hasNext(); iter.next()) {
-            var node = iter.edge.node();
-            if (pendingActivitiesRE.test(node.className())) {
-                if ("Pending activities / 3 entries" === node.name()) {
-                    if (pendingActivitiesFound)
-                        return InspectorTest.fail("second " + node.name());
-                    pendingActivitiesFound = true;
-                    InspectorTest.log("SUCCESS: found " + node.name());
-                    checkPendingActivities(node);
-                } else
-                    return InspectorTest.fail("unexpected 'Pending activities': " + node.name());
-            }
-        }
-        InspectorTest.completeTest();
-    }
-
-    function checkPendingActivities(groupNode)
-    {
-        var mediaQuryListCount = 0;
-        for (var iter = groupNode.edges(); iter.hasNext(); iter.next()) {
-            var node = iter.edge.node();
-            if (node.name() === "MediaQueryList")
-                ++mediaQuryListCount;
-        }
-        if (mediaQuryListCount === 2)
-            InspectorTest.log("SUCCESS: found " + mediaQuryListCount + " MediaQueryLists in " + groupNode.name());
-        else
-            return InspectorTest.fail("unexpected MediaQueryLists count: " + mediaQuryListCount);
-
-    }
-
-    InspectorTest.takeHeapSnapshot(checkHeapSnapshot);
-}
-</script>
-</head>
-<body onload="createTwoActiveDOMObjectsAndRunTest()">
-<p>Test that all ActiveDOMObjects with pending activities will get into one group in the heap snapshot. <a href="https://crbug.com/426809">Bug 426809.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js
new file mode 100644
index 0000000..7cab593
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.js
@@ -0,0 +1,62 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+    `Test that all ActiveDOMObjects with pending activities will get into one group in the heap snapshot. Bug 426809.`);
+
+  await session.evaluate(`
+    function onChange(e) {
+      console.log('onChange ' + e.matches);
+    }
+    var m = window.matchMedia('(min-width: 1400px)');
+    m.addListener(onChange);
+    m = window.matchMedia('(min-height: 1800px)');
+    m.addListener(onChange);
+    console.log('Created 2 MediaQueryList elements');
+  `);
+
+  function checkPendingActivities(groupNode) {
+    var mediaQuryListCount = 0;
+    for (var iter = groupNode.edges(); iter.hasNext(); iter.next()) {
+      var node = iter.edge.node();
+      if (node.name() === 'MediaQueryList')
+        ++mediaQuryListCount;
+    }
+    if (mediaQuryListCount === 2)
+      testRunner.log('SUCCESS: found ' + mediaQuryListCount + ' MediaQueryLists in ' + groupNode.name());
+    else
+      return testRunner.fail('unexpected MediaQueryLists count: ' + mediaQuryListCount);
+  }
+
+  var Helper = await testRunner.loadScript('resources/heap-snapshot-common.js');
+  var helper = await Helper(testRunner, session);
+
+  var snapshot = await helper.takeHeapSnapshot();
+  var node;
+  for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
+    if (it.node.name() === '(Pending activities group)') {
+      node = it.node;
+      break;
+    }
+  }
+  if (node)
+    testRunner.log('SUCCESS: found ' + node.name());
+  else
+    return testRunner.fail(`cannot find '(Pending activities group)'`);
+
+  var pendingActivitiesRE = /^Pending activities/;
+  var pendingActivitiesFound = false;
+  for (var iter = node.edges(); iter.hasNext(); iter.next()) {
+    var node = iter.edge.node();
+    if (pendingActivitiesRE.test(node.className())) {
+      if ('Pending activities / 3 entries' === node.name()) {
+        if (pendingActivitiesFound)
+          return testRunner.fail('second ' + node.name());
+        pendingActivitiesFound = true;
+        testRunner.log('SUCCESS: found ' + node.name());
+        checkPendingActivities(node);
+      } else {
+        return testRunner.fail(`unexpected 'Pending activities': ${node.name()}`);
+      }
+    }
+  }
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree-expected.txt
index 82cc969..8042a9f 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree-expected.txt
@@ -1,6 +1,5 @@
 Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. Bug 107819.
-
- Took heap snapshot
+Took heap snapshot
 Parsed snapshot
 SUCCESS: found (Detached DOM trees)
 SUCCESS: found Detached DOM tree / 3 entries
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html
deleted file mode 100644
index 0bd34f6..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function createDetachedDomTreeAndRunTest()
-{
-    if (window.gc)
-        window.gc();
-    window.retaining_wrapper = document.createElement("div");
-    var t = document.createElement("div");
-    retaining_wrapper.appendChild(t);
-    t.appendChild(document.createElement("div"));
-
-    if (window.testRunner)
-        runTest();
-}
-
-function test()
-{
-    InspectorTest.importScript("../../../../inspector-protocol/heap-profiler/resources/heap-snapshot-common.js");
-
-    function checkHeapSnapshot(snapshot) {
-        var node;
-        for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
-            if (it.node.name() === "(Detached DOM trees)") {
-                node = it.node;
-                break;
-            }
-        }
-        if (node)
-            InspectorTest.log("SUCCESS: found " + node.name());
-        else
-            return InspectorTest.fail("cannot find detached DOM trees root");
-
-        var detachedDOMTreeRE = /^Detached DOM tree/;
-        var detachedDomTreeFound = false;
-        for (var iter = node.edges(); iter.hasNext(); iter.next()) {
-            var node = iter.edge.node();
-            if (detachedDOMTreeRE.test(node.className())) {
-                if ("Detached DOM tree / 3 entries" === node.name()) {
-                    if (detachedDomTreeFound)
-                        return InspectorTest.fail("second " + node.name());
-                    detachedDomTreeFound = true;
-                    InspectorTest.log("SUCCESS: found " + node.name());
-                    checkDetachedDOMTreeNodes(node);
-                } else
-                    return InspectorTest.fail("unexpected detached DOM tree: " + node.name());
-            }
-        }
-        InspectorTest.completeTest();
-    }
-
-    function checkDetachedDOMTreeNodes(treeNode)
-    {
-        var divCount = 0;
-        for (var iter = treeNode.edges(); iter.hasNext(); iter.next()) {
-            var node = iter.edge.node();
-            if (node.name() === "HTMLDivElement")
-                ++divCount;
-            else
-                return InspectorTest.fail("unexpected DOM wrapper: " + node.name());
-        }
-        if (divCount === 3)
-            InspectorTest.log("SUCCESS: found " + divCount + " DIVs in " + treeNode.name());
-        else
-            return InspectorTest.fail("unexpected DIV count: " + divCount);
-
-    }
-
-    InspectorTest.takeHeapSnapshot(checkHeapSnapshot);
-}
-</script>
-</head>
-<body onload="createDetachedDomTreeAndRunTest()">
-<p>Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. <a href="https://bugs.webkit.org/show_bug.cgi?id=107819">Bug 107819.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js
new file mode 100644
index 0000000..fa60d3d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.js
@@ -0,0 +1,61 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      `Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. Bug 107819.`);
+
+  await session.evaluate(`
+    if (window.gc)
+      window.gc();
+    window.retaining_wrapper = document.createElement('div');
+    var t = document.createElement('div');
+    retaining_wrapper.appendChild(t);
+    t.appendChild(document.createElement('div'));
+  `);
+
+  function checkDetachedDOMTreeNodes(treeNode) {
+    var divCount = 0;
+    for (var iter = treeNode.edges(); iter.hasNext(); iter.next()) {
+      var node = iter.edge.node();
+      if (node.name() === 'HTMLDivElement')
+        ++divCount;
+      else
+        return testRunner.fail('unexpected DOM wrapper: ' + node.name());
+    }
+    if (divCount === 3)
+      testRunner.log('SUCCESS: found ' + divCount + ' DIVs in ' + treeNode.name());
+    else
+      return testRunner.fail('unexpected DIV count: ' + divCount);
+  }
+
+  var Helper = await testRunner.loadScript('resources/heap-snapshot-common.js');
+  var helper = await Helper(testRunner, session);
+
+  var snapshot = await helper.takeHeapSnapshot();
+  var node;
+  for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
+    if (it.node.name() === '(Detached DOM trees)') {
+      node = it.node;
+      break;
+    }
+  }
+  if (node)
+    testRunner.log('SUCCESS: found ' + node.name());
+  else
+    return testRunner.fail('cannot find detached DOM trees root');
+
+  var detachedDOMTreeRE = /^Detached DOM tree/;
+  var detachedDomTreeFound = false;
+  for (var iter = node.edges(); iter.hasNext(); iter.next()) {
+    var node = iter.edge.node();
+    if (detachedDOMTreeRE.test(node.className())) {
+      if ('Detached DOM tree / 3 entries' === node.name()) {
+        if (detachedDomTreeFound)
+          return testRunner.fail('second ' + node.name());
+        detachedDomTreeFound = true;
+        testRunner.log('SUCCESS: found ' + node.name());
+        checkDetachedDOMTreeNodes(node);
+      } else
+        return testRunner.fail('unexpected detached DOM tree: ' + node.name());
+    }
+  }
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt
index 8dee329..4608a90 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener-expected.txt
@@ -1,7 +1,6 @@
 Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. Bug 107819.
-
- Took heap snapshot
+Took heap snapshot
 Parsed snapshot
 SUCCESS: found myEventListener
-SUCCESS: found link from HTMLBodyElement to HTMLBodyElement
+SUCCESS: found link from HTMLBodyElement to myEventListener
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html
deleted file mode 100644
index ff41be3..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function addEventListenerAndRunTest()
-{
-    function myEventListener(e) {
-        console.log("myEventListener");
-    }
-    document.body.addEventListener("click", myEventListener, true);
-
-    runTest();
-}
-
-function test()
-{
-    InspectorTest.importScript("../../../../inspector-protocol/heap-profiler/resources/heap-snapshot-common.js");
-
-    function checkHeapSnapshot(snapshot)
-    {
-        var node;
-        for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
-            if (it.node.type() === "closure" && it.node.name() === "myEventListener") {
-                node = it.node;
-                break;
-            }
-        }
-        if (node)
-            InspectorTest.log("SUCCESS: found " + node.name());
-        else
-            return fail("cannot find detached DOM trees root");
-
-        var nativeRetainers = 0;
-        for (var iter = node.retainers(); iter.hasNext(); iter.next()) {
-            var node = iter.retainer.node();
-            var retainingEdge = iter.retainer;
-            if (retainingEdge.isInternal() && retainingEdge.name() === "native") {
-                if (++nativeRetainers === 1) {
-                    var retainerName = retainingEdge.node().name();
-                    if (retainerName === "HTMLBodyElement")
-                        InspectorTest.log("SUCCESS: found link from HTMLBodyElement to " + node.name());
-                    else
-                        return fail("unexpected retainer of " + node.name() + ": " + retainerName);
-                } else
-                    return fail("too many retainers of " + node.name());
-            } else if (!retainingEdge.isWeak())
-                return fail("unexpected retaining edge of " + node.name() + " type: " + retainingEdge.type() + ", name: " + retainingEdge.name());
-        }
-        if (!nativeRetainers)
-            return fail("cannot find HTMLBodyElement among retainers of " + node.name());
-        InspectorTest.completeTest();
-    }
-
-    function fail(message) {
-        InspectorTest.log("FAIL: " + message);
-        InspectorTest.completeTest();
-    }
-
-    InspectorTest.takeHeapSnapshot(checkHeapSnapshot);
-}
-</script>
-</head>
-<body onload="addEventListenerAndRunTest()">
-<p>Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. <a href="https://bugs.webkit.org/show_bug.cgi?id=107819">Bug 107819.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js
new file mode 100644
index 0000000..286057b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js
@@ -0,0 +1,49 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      `Test that all nodes from the detached DOM tree will get into one group in the heap snapshot. Bug 107819.`);
+
+  await session.evaluate(`
+    function addEventListenerAndRunTest() {
+      function myEventListener(e) {
+        console.log('myEventListener');
+      }
+      document.body.addEventListener('click', myEventListener, true);
+    }
+    addEventListenerAndRunTest();
+  `);
+
+  var Helper = await testRunner.loadScript('resources/heap-snapshot-common.js');
+  var helper = await Helper(testRunner, session);
+
+  var snapshot = await helper.takeHeapSnapshot();
+  var node;
+  for (var it = snapshot._allNodes(); it.hasNext(); it.next()) {
+    if (it.node.type() === 'closure' && it.node.name() === 'myEventListener') {
+      node = it.node;
+      break;
+    }
+  }
+  if (node)
+    testRunner.log('SUCCESS: found ' + node.name());
+  else
+    return testRunner.fail('cannot find detached DOM trees root');
+
+  var nativeRetainers = 0;
+  for (var iter = node.retainers(); iter.hasNext(); iter.next()) {
+    var retainingEdge = iter.retainer;
+    if (retainingEdge.isInternal() && retainingEdge.name() === 'native') {
+      if (++nativeRetainers === 1) {
+        var retainerName = retainingEdge.node().name();
+        if (retainerName === 'HTMLBodyElement')
+          testRunner.log('SUCCESS: found link from HTMLBodyElement to ' + node.name());
+        else
+          return testRunner.fail('unexpected retainer of ' + node.name() + ': ' + retainerName);
+      } else
+        return testRunner.fail('too many retainers of ' + node.name());
+    } else if (!retainingEdge.isWeak())
+      return testRunner.fail('unexpected retaining edge of ' + node.name() + ' type: ' + retainingEdge.type() + ', name: ' + retainingEdge.name());
+  }
+  if (!nativeRetainers)
+    return testRunner.fail('cannot find HTMLBodyElement among retainers of ' + node.name());
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/resources/heap-snapshot-common.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/resources/heap-snapshot-common.js
index f9363027..5927bf1e 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/resources/heap-snapshot-common.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/resources/heap-snapshot-common.js
@@ -1,53 +1,40 @@
-// This script is supposed to be evaluated in dummy inspector front-end which is loaded from
-// ../../../http/tests/inspector-protocol/resources/protocol-test.html and the relative paths
-// below are relative to that location.
+(async function(testRunner, session) {
+  self['Common'] = {};
+  self['TextUtils'] = {};
+  self['HeapSnapshotModel'] = {};
+  self['HeapSnapshotWorker'] = {};
 
-if (!window.WebInspector)
-    window.WebInspector = {};
+  // This script is supposed to be evaluated in inspector-protocol/heap-profiler tests
+  // and the relative paths below are relative to that location.
+  await testRunner.loadScript('../../../Source/devtools/front_end/platform/utilities.js');
+  await testRunner.loadScript('../../../Source/devtools/front_end/common/UIString.js');
+  await testRunner.loadScript('../../../Source/devtools/front_end/heap_snapshot_model/HeapSnapshotModel.js');
+  await testRunner.loadScript('../../../Source/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js');
+  await testRunner.loadScript('../../../Source/devtools/front_end/text_utils/TextUtils.js');
+  await testRunner.loadScript('../../../Source/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js');
 
-self['Common'] = {};
-self['TextUtils'] = {};
-self['HeapSnapshotModel'] = {};
-self['HeapSnapshotWorker'] = {};
-
-InspectorTest.importScript("../../../../../Source/devtools/front_end/platform/utilities.js");
-InspectorTest.importScript("../../../../../Source/devtools/front_end/common/UIString.js");
-InspectorTest.importScript("../../../../../Source/devtools/front_end/heap_snapshot_model/HeapSnapshotModel.js");
-InspectorTest.importScript("../../../../../Source/devtools/front_end/heap_snapshot_worker/HeapSnapshot.js");
-InspectorTest.importScript("../../../../../Source/devtools/front_end/text_utils/TextUtils.js");
-InspectorTest.importScript("../../../../../Source/devtools/front_end/heap_snapshot_worker/HeapSnapshotLoader.js");
-
-InspectorTest.fail = function(message)
-{
-    InspectorTest.log("FAIL: " + message);
-    InspectorTest.completeTest();
-}
-
-InspectorTest._takeHeapSnapshotInternal = function(command, callback)
-{
+  async function takeHeapSnapshotInternal(command) {
     var loader = new HeapSnapshotWorker.HeapSnapshotLoader();
-    InspectorTest.eventHandler["HeapProfiler.addHeapSnapshotChunk"] = function(messageObject)
-    {
-        loader.write(messageObject["params"]["chunk"]);
+    function onChunk(messageObject) {
+      loader.write(messageObject['params']['chunk']);
     }
+    session.protocol.HeapProfiler.onAddHeapSnapshotChunk(onChunk);
+    await command();
+    session.protocol.HeapProfiler.offAddHeapSnapshotChunk(onChunk);
+    testRunner.log('Took heap snapshot');
+    loader.close();
+    var snapshot = loader.buildSnapshot(false);
+    testRunner.log('Parsed snapshot');
+    return snapshot;
+  }
 
-    function didTakeHeapSnapshot(messageObject)
-    {
-        InspectorTest.log("Took heap snapshot");
-        loader.close();
-        var snapshot = loader.buildSnapshot(false);
-        InspectorTest.log("Parsed snapshot");
-        callback(snapshot);
+  return {
+    takeHeapSnapshot: function() {
+      return takeHeapSnapshotInternal(() => session.protocol.HeapProfiler.takeHeapSnapshot());
+    },
+
+    stopRecordingHeapTimeline: function() {
+      return takeHeapSnapshotInternal(() => session.protocol.HeapProfiler.stopTrackingHeapObjects());
     }
-    InspectorTest.sendCommand(command, {}, didTakeHeapSnapshot);
-}
-
-InspectorTest.takeHeapSnapshot = function(callback)
-{
-    InspectorTest._takeHeapSnapshotInternal("HeapProfiler.takeHeapSnapshot", callback);
-}
-
-InspectorTest.stopRecordingHeapTimeline = function(callback)
-{
-    InspectorTest._takeHeapSnapshotInternal("HeapProfiler.stopTrackingHeapObjects", callback);
-}
+  };
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler-expected.txt
index e417d95..5efaaa1 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler-expected.txt
@@ -1,5 +1,4 @@
 Test sampling heap profiler.
-
 Sampling started
 Sampling stopped
 size=0   testMain:10:17
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.html
deleted file mode 100644
index 4dea3a21..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function testMain()
-{
-    makeDeepCallStack(10, junkGenerator);
-}
-
-function makeDeepCallStack(depth, action)
-{
-    if (depth)
-        makeDeepCallStack(depth - 1, action);
-    else
-        action();
-}
-
-function junkGenerator()
-{
-    var junkArray = new Array(3000);
-    window.junkArray = junkArray;
-    for (var i = 1; i < junkArray.length; ++i)
-        junkArray[i] = new Array(i);
-}
-
-function test()
-{
-    InspectorTest.sendCommand("HeapProfiler.startSampling", {}, samplingStarted);
-
-    function samplingStarted()
-    {
-        InspectorTest.log("Sampling started");
-        InspectorTest.sendCommand("Runtime.evaluate", { expression: "testMain()" }, stopSampling);
-    }
-
-    function stopSampling()
-    {
-        InspectorTest.sendCommand("HeapProfiler.stopSampling", {}, samplingStopped);
-    }
-
-    function samplingStopped(message)
-    {
-        InspectorTest.log("Sampling stopped");
-        var profile = message.result.profile;
-        var head = profile.head;
-        logNode(findNode(head, "testMain", 1));
-        logNode(findNode(head, "makeDeepCallStack", 11));
-        logNode(findNode(head, "junkGenerator", 12));
-
-        InspectorTest.completeTest();
-    }
-
-    function findNode(root, name, depth)
-    {
-        if (depth < 1 && root.callFrame.functionName === name)
-            return root;
-        return root.children.reduce((found, child) => found || findNode(child, name, depth - 1), null);
-    }
-
-    function logNode(node)
-    {
-        var size = typeof node.selfSize === "number" ? node.selfSize ? ">0" : "=0" : "-";
-        InspectorTest.log(`size${size}   ${node.callFrame.functionName}:${node.callFrame.lineNumber}:${node.callFrame.columnNumber}`);
-    }
-
-    //# sourceURL=sampling-heap-profiler.html
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Test sampling heap profiler.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.js
new file mode 100644
index 0000000..427b0df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/sampling-heap-profiler.js
@@ -0,0 +1,59 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(`Test sampling heap profiler.`);
+
+  function findNode(root, name, depth) {
+    if (depth < 1 && root.callFrame.functionName === name)
+      return root;
+    return root.children.reduce((found, child) => found || findNode(child, name, depth - 1), null);
+  }
+
+  function logNode(node) {
+    var size = typeof node.selfSize === 'number' ? node.selfSize ? '>0' : '=0' : '-';
+    testRunner.log(`size${size}   ${node.callFrame.functionName}:${node.callFrame.lineNumber}:${node.callFrame.columnNumber}`);
+  }
+
+  await dp.HeapProfiler.startSampling();
+  testRunner.log('Sampling started');
+  await session.evaluate(`
+
+
+
+
+
+
+
+
+
+function testMain()
+{
+    makeDeepCallStack(10, junkGenerator);
+}
+
+function makeDeepCallStack(depth, action)
+{
+    if (depth)
+        makeDeepCallStack(depth - 1, action);
+    else
+        action();
+}
+
+function junkGenerator()
+{
+    var junkArray = new Array(3000);
+    window.junkArray = junkArray;
+    for (var i = 1; i < junkArray.length; ++i)
+        junkArray[i] = new Array(i);
+}
+
+testMain();
+  `);
+
+  var message = await dp.HeapProfiler.stopSampling();
+  testRunner.log('Sampling stopped');
+  var profile = message.result.profile;
+  var head = profile.head;
+  logNode(findNode(head, 'testMain', 1));
+  logNode(findNode(head, 'makeDeepCallStack', 11));
+  logNode(findNode(head, 'junkGenerator', 12));
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot-expected.txt
index d48d9ba..16a424e 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot-expected.txt
@@ -1,5 +1,3 @@
 Test that heap profiler doesn't crash while taking snapshot on a page where iframe was navigated to a new location after storing a hold of a function from the previous page. Bug 103076.
-
- 
 SUCCESS: didTakeHeapSnapshot
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.html b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.html
deleted file mode 100644
index 6805fde..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-var fooRef;
-function storeFunctionRefAndNavigateIFrame()
-{
-    var frame = document.getElementById("myframe");
-    fooRef = frame.contentWindow.foo;
-    frame.src = "about:blank";
-    frame.onload = didNavigate;
-}
-
-function didNavigate()
-{
-    runTest();
-}
-function test()
-{
-    function didTakeHeapSnapshot(messageObject)
-    {
-        InspectorTest.log("SUCCESS: didTakeHeapSnapshot");
-        InspectorTest.completeTest();
-    }
-
-    InspectorTest.sendCommand("Profiler.takeHeapSnapshot", { "reportProgress": false }, didTakeHeapSnapshot);
-}
-</script>
-</head>
-<body>
-<p>Test that heap profiler doesn't crash while taking snapshot on a page where iframe was navigated to a new location after
-storing a hold of a function from the previous page. <a href="https://bugs.webkit.org/show_bug.cgi?id=103076">Bug 103076.</p>
-<iframe id="myframe" src="resources/page-with-function.html" onload="storeFunctionRefAndNavigateIFrame()"></iframe>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.js b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.js
new file mode 100644
index 0000000..8a2382a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/heap-profiler/take-heap-snapshot.js
@@ -0,0 +1,22 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+    'Test that heap profiler doesn\'t crash while taking snapshot on a page where iframe was navigated to a new location after ' +
+    'storing a hold of a function from the previous page. Bug 103076.');
+
+  await session.evaluateAsync(`
+    var frame = document.createElement('iframe');
+    frame.src = '${testRunner.url('resources/page-with-function.html')}';
+    document.body.appendChild(frame);
+    var loadPromise = new Promise(f => frame.onload = f);
+    var storeFunctionRefAndNavigateIFramePromise = loadPromise.then(() => {
+      window.fooRef = frame.contentWindow.foo;
+      frame.src = 'about:blank';
+      return new Promise(f => frame.onload = f);
+    });
+    storeFunctionRefAndNavigateIFramePromise
+  `);
+
+  await dp.Profiler.takeHeapSnapshot({reportProgress: false});
+  testRunner.log('SUCCESS: didTakeHeapSnapshot');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/storage/websql/fts-crash-703507.html b/third_party/WebKit/LayoutTests/storage/websql/fts-crash-703507.html
index 4d77c6c..d08e3512 100644
--- a/third_party/WebKit/LayoutTests/storage/websql/fts-crash-703507.html
+++ b/third_party/WebKit/LayoutTests/storage/websql/fts-crash-703507.html
@@ -15,7 +15,7 @@
     database.transaction(testCase.step_func(transaction => {
       transaction.executeSql(
           'DROP TABLE IF EXISTS main;', [], () => {},
-          testCase.unreached_func('FTS3 table drop should not fail'));
+          testCase.unreached_func('Table drop should not fail'));
       transaction.executeSql(
           'CREATE VIRTUAL TABLE main USING FTS3(fts3table);', [], () => {},
           testCase.unreached_func('FTS3 table creation should not fail'));
diff --git a/third_party/WebKit/LayoutTests/storage/websql/fts-pointer-leak-742407.html b/third_party/WebKit/LayoutTests/storage/websql/fts-pointer-leak-742407.html
new file mode 100644
index 0000000..95add45
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/storage/websql/fts-pointer-leak-742407.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>WebSQL: FTS3 Pointer Leak in bug 742407</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+async_test(testCase => {
+  const database = openDatabase(
+      'Fts3CrashTest', '1.0', 'Database for FTS3 crash test', 1024 * 1024);
+  assert_not_equals(database, null, 'openDatabase should not fail');
+
+  database.transaction(testCase.step_func(transaction => {
+    transaction.executeSql(
+        'DROP TABLE IF EXISTS main;', [], () => {},
+        testCase.unreached_func('FTS3 table drop should not fail'));
+    transaction.executeSql(
+        'CREATE VIRTUAL TABLE main USING FTS3(fts3table);', [], () => {},
+        testCase.unreached_func('FTS3 table creation should not fail'));
+    transaction.executeSql(
+        "INSERT INTO main VALUES (x'0000000000000000');", [], () => {},
+        testCase.unreached_func('FTS3 table insertion should not fail'));
+
+    transaction.executeSql(
+        'SELECT hex(main) AS h FROM main;', [],
+        testCase.step_func((_, result) => {
+          assert_equals(result.rows.item(0).h, '');
+          testCase.done();
+        }), testCase.unreached_func('FTS3 select should not fail'));
+
+  }));
+}, `SELECT should not return the value of an FTS3 pointer column`);
+
+</script>
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index db953b7..8eb8aa59 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -11,7 +11,7 @@
 
 from name_utilities import (
     enum_for_css_keyword, enum_type_name, enum_value_name, class_member_name, method_name,
-    class_name, join_name
+    class_name, join_names
 )
 from itertools import chain
 
@@ -77,8 +77,8 @@
         self.fields = fields
         self.parent = None
 
-        self.type_name = class_name(join_name('style', name, 'data'))
-        self.member_name = class_member_name(join_name(name, 'data'))
+        self.type_name = class_name(['style', name, 'data'])
+        self.member_name = class_member_name([name, 'data'])
         self.num_32_bit_words_for_bit_fields = _num_32_bit_words_for_bit_fields(
             field for field in fields if field.is_bit_field
         )
@@ -188,17 +188,17 @@
             self.is_independent = kwargs.pop('independent')
             assert self.is_inherited or not self.is_independent, 'Only inherited fields can be independent'
 
-            self.is_inherited_method_name = method_name(join_name(name_for_methods, 'is inherited'))
+            self.is_inherited_method_name = method_name([name_for_methods, 'is inherited'])
 
         # Method names
         # TODO(nainar): Method name generation is inconsistent. Fix.
         self.getter_method_name = getter_method_name
         self.setter_method_name = setter_method_name
-        self.internal_getter_method_name = method_name(join_name(self.name, 'Internal'))
-        self.internal_mutable_method_name = method_name(join_name('Mutable', name_for_methods, 'Internal'))
-        self.internal_setter_method_name = method_name(join_name(setter_method_name, 'Internal'))
+        self.internal_getter_method_name = method_name([self.name, 'internal'])
+        self.internal_mutable_method_name = method_name(['mutable', name_for_methods, 'internal'])
+        self.internal_setter_method_name = method_name([setter_method_name, 'internal'])
         self.initial_method_name = initial_method_name
-        self.resetter_method_name = method_name(join_name('Reset', name_for_methods))
+        self.resetter_method_name = method_name(['reset', name_for_methods])
         self.default_generated_functions = default_generated_functions
         # If the size of the field is not None, it means it is a bit field
         self.is_bit_field = self.size is not None
@@ -415,7 +415,7 @@
     Create the field used for an inheritance fast path from an independent CSS property,
     and return the Field object.
     """
-    name_for_methods = join_name(property_['name_for_methods'], 'is inherited')
+    name_for_methods = join_names(property_['name_for_methods'], 'is', 'inherited')
     return Field(
         'inherited_flag',
         name_for_methods,
@@ -429,8 +429,8 @@
         custom_compare=False,
         mutable=False,
         getter_method_name=method_name(name_for_methods),
-        setter_method_name=method_name(join_name('set', name_for_methods)),
-        initial_method_name=method_name(join_name('initial', name_for_methods)),
+        setter_method_name=method_name(['set', name_for_methods]),
+        initial_method_name=method_name(['initial', name_for_methods]),
         default_generated_functions=property_["default_generated_functions"]
     )
 
diff --git a/third_party/WebKit/Source/build/scripts/name_utilities.py b/third_party/WebKit/Source/build/scripts/name_utilities.py
index ba2955e..07a8ef0 100644
--- a/third_party/WebKit/Source/build/scripts/name_utilities.py
+++ b/third_party/WebKit/Source/build/scripts/name_utilities.py
@@ -132,45 +132,68 @@
                       upper_first_letter(name))
 
 
-def upper_camel_case(name):
-    return ''.join(upper_first_letter(word) for word in split_name(name))
+def join_names(*names):
+    """Given a list of names, join them into a single space-separated name."""
+    result = []
+    for name in names:
+        result.extend(split_name(name))
+    return ' '.join(result)
 
 
-def lower_camel_case(name):
-    return lower_first_letter(upper_camel_case(name))
+def naming_style(f):
+    """Decorator for name utility functions.
+
+    Wraps a name utility function in a function that takes one or more names,
+    splits them into a list of words, and passes the list to the utility function.
+    """
+    def inner(name_or_names):
+        names = name_or_names if isinstance(name_or_names, list) else [name_or_names]
+        words = []
+        for name in names:
+            words.extend(split_name(name))
+        return f(words)
+    return inner
 
 
-def snake_case(name):
-    return '_'.join(word.lower() for word in split_name(name))
+@naming_style
+def upper_camel_case(words):
+    return ''.join(upper_first_letter(word) for word in words)
+
+
+@naming_style
+def lower_camel_case(words):
+    return lower_first_letter(upper_camel_case(words))
+
+
+@naming_style
+def snake_case(words):
+    return '_'.join(word.lower() for word in words)
 
 
 # Use these high level naming functions which describe the semantics of the name,
 # rather than a particular style.
 
 
-def enum_type_name(name):
-    return upper_camel_case(name)
+@naming_style
+def enum_type_name(words):
+    return upper_camel_case(words)
 
 
-def enum_value_name(name):
-    return 'k' + upper_camel_case(name)
+@naming_style
+def enum_value_name(words):
+    return 'k' + upper_camel_case(words)
 
 
-def class_name(name):
-    return upper_camel_case(name)
+@naming_style
+def class_name(words):
+    return upper_camel_case(words)
 
 
-def class_member_name(name):
-    return snake_case(name) + "_"
+@naming_style
+def class_member_name(words):
+    return snake_case(words) + "_"
 
 
-def method_name(name):
-    return upper_camel_case(name)
-
-
-def join_name(*names):
-    """Given a list of names, join them into a single space-separated name."""
-    result = []
-    for name in names:
-        result.extend(split_name(name))
-    return ' '.join(result)
+@naming_style
+def method_name(words):
+    return upper_camel_case(words)
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
index 719dc16..0bc6b6c 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
@@ -61,6 +61,8 @@
 
   PaintLayerScrollableArea* scrollable_area =
       ancestor_overflow_layer->GetScrollableArea();
+  if (!scrollable_area)
+    return nullptr;
   auto it = scrollable_area->GetStickyConstraintsMap().find(obj->Layer());
   if (it == scrollable_area->GetStickyConstraintsMap().end())
     return nullptr;
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositingRequirementsUpdater.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositingRequirementsUpdater.cpp
index 30520de..3cfa7d8 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositingRequirementsUpdater.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositingRequirementsUpdater.cpp
@@ -194,8 +194,11 @@
     // We ignore LCD text here because we are required to composite
     // scroll-dependant fixed position elements with composited descendants for
     // correctness - even if we lose LCD.
+    //
+    // TODO(smcgruer): Only composite fixed if needed (http://crbug.com/742213)
     const bool ignore_lcd_text = true;
-    if (compositing_reason_finder.RequiresCompositingForScrollDependentPosition(
+    if (layer->GetLayoutObject().Style()->GetPosition() == EPosition::kFixed ||
+        compositing_reason_finder.RequiresCompositingForScrollDependentPosition(
             layer, ignore_lcd_text)) {
       subtree_reasons |=
           kCompositingReasonPositionFixedOrStickyWithCompositedDescendants;
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
index ed9f69a..755d78c 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -452,18 +452,18 @@
   // relative to each other.  The first item appended appears at the top of the
   // overflow menu.
   overflow_list_->AppendChild(play_button_->CreateOverflowElement(
-      *this, new MediaControlPlayButtonElement(*this)));
+      new MediaControlPlayButtonElement(*this)));
   overflow_list_->AppendChild(fullscreen_button_->CreateOverflowElement(
-      *this, new MediaControlFullscreenButtonElement(*this)));
+      new MediaControlFullscreenButtonElement(*this)));
   overflow_list_->AppendChild(download_button_->CreateOverflowElement(
-      *this, new MediaControlDownloadButtonElement(*this)));
+      new MediaControlDownloadButtonElement(*this)));
   overflow_list_->AppendChild(mute_button_->CreateOverflowElement(
-      *this, new MediaControlMuteButtonElement(*this)));
+      new MediaControlMuteButtonElement(*this)));
   overflow_list_->AppendChild(cast_button_->CreateOverflowElement(
-      *this, new MediaControlCastButtonElement(*this, false)));
+      new MediaControlCastButtonElement(*this, false)));
   overflow_list_->AppendChild(
       toggle_closed_captions_button_->CreateOverflowElement(
-          *this, new MediaControlToggleClosedCaptionsButtonElement(*this)));
+          new MediaControlToggleClosedCaptionsButtonElement(*this)));
 }
 
 Node::InsertionNotificationRequest MediaControlsImpl::InsertedInto(
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.cpp b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.cpp
index ed270064..28209ac4 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.cpp
@@ -34,7 +34,6 @@
 }
 
 HTMLElement* MediaControlInputElement::CreateOverflowElement(
-    MediaControlsImpl& media_controls,
     MediaControlInputElement* button) {
   if (!button)
     return nullptr;
@@ -42,11 +41,10 @@
   // We don't want the button visible within the overflow menu.
   button->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
 
-  overflow_menu_text_ = Text::Create(media_controls.GetDocument(),
-                                     button->GetOverflowMenuString());
+  overflow_menu_text_ =
+      Text::Create(GetDocument(), button->GetOverflowMenuString());
 
-  HTMLLabelElement* element =
-      HTMLLabelElement::Create(media_controls.GetDocument());
+  HTMLLabelElement* element = HTMLLabelElement::Create(GetDocument());
   element->SetShadowPseudoId(
       AtomicString("-internal-media-controls-overflow-menu-list-item"));
   // Appending a button to a label element ensures that clicks on the label
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.h b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.h
index 3c59b03..abfe535 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.h
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElement.h
@@ -23,8 +23,7 @@
   static bool ShouldRecordDisplayStates(const HTMLMediaElement&);
 
   // Creates an overflow menu element with the given button as a child.
-  HTMLElement* CreateOverflowElement(MediaControlsImpl&,
-                                     MediaControlInputElement*);
+  HTMLElement* CreateOverflowElement(MediaControlInputElement*);
 
   // Implements MediaControlElementBase.
   void SetOverflowElementIsWanted(bool) final;
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElementTest.cpp b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElementTest.cpp
index ff4a4da..a21bdf5 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElementTest.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlInputElementTest.cpp
@@ -181,7 +181,7 @@
 
   Persistent<HTMLElement> overflow_container =
       ControlInputElement().CreateOverflowElement(
-          MediaControls(), new MediaControlInputElementImpl(MediaControls()));
+          new MediaControlInputElementImpl(MediaControls()));
 
   ControlInputElement().SetIsWanted(true);
   ControlInputElement().SetDoesFit(false);
@@ -198,7 +198,7 @@
 
   Persistent<HTMLElement> overflow_container =
       ControlInputElement().CreateOverflowElement(
-          MediaControls(), new MediaControlInputElementImpl(MediaControls()));
+          new MediaControlInputElementImpl(MediaControls()));
 
   ControlInputElement().SetIsWanted(true);
   ControlInputElement().SetDoesFit(false);
@@ -220,7 +220,7 @@
 
   Persistent<HTMLElement> overflow_container =
       ControlInputElement().CreateOverflowElement(
-          MediaControls(), new MediaControlInputElementImpl(MediaControls()));
+          new MediaControlInputElementImpl(MediaControls()));
 
   ControlInputElement().SetIsWanted(true);
   ControlInputElement().SetDoesFit(true);
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
index e786161..9a8d89f5 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImage.cpp
@@ -154,7 +154,7 @@
 
   frames_[index].orientation_ = source_.OrientationAtIndex(index);
   frames_[index].have_metadata_ = true;
-  frames_[index].is_complete_ = source_.FrameIsCompleteAtIndex(index);
+  frames_[index].is_complete_ = source_.FrameIsReceivedAtIndex(index);
   if (RepetitionCount(false) != kAnimationNone)
     frames_[index].duration_ = source_.FrameDurationAtIndex(index);
   frames_[index].has_alpha_ = source_.FrameHasAlphaAtIndex(index);
@@ -349,12 +349,12 @@
   return DecodeAndCacheFrame(index);
 }
 
-bool BitmapImage::FrameIsCompleteAtIndex(size_t index) const {
+bool BitmapImage::FrameIsReceivedAtIndex(size_t index) const {
   if (index < frames_.size() && frames_[index].have_metadata_ &&
       frames_[index].is_complete_)
     return true;
 
-  return source_.FrameIsCompleteAtIndex(index);
+  return source_.FrameIsReceivedAtIndex(index);
 }
 
 float BitmapImage::FrameDurationAtIndex(size_t index) const {
@@ -406,7 +406,7 @@
 }
 
 bool BitmapImage::CurrentFrameIsComplete() {
-  return FrameIsCompleteAtIndex(CurrentFrame());
+  return FrameIsReceivedAtIndex(CurrentFrame());
 }
 
 bool BitmapImage::CurrentFrameIsLazyDecoded() {
@@ -464,7 +464,7 @@
 
   // Don't advance the animation to an incomplete frame.
   size_t next_frame = (current_frame_ + 1) % FrameCount();
-  if (!all_data_received_ && !FrameIsCompleteAtIndex(next_frame))
+  if (!all_data_received_ && !FrameIsReceivedAtIndex(next_frame))
     return;
 
   // Don't advance past the last frame if we haven't decoded the whole image
@@ -517,7 +517,7 @@
     // case we need to skip some frames entirely.  Remember not to advance
     // to an incomplete frame.
     for (size_t frame_after_next = (next_frame + 1) % FrameCount();
-         FrameIsCompleteAtIndex(frame_after_next);
+         FrameIsReceivedAtIndex(frame_after_next);
          frame_after_next = (next_frame + 1) % FrameCount()) {
       // Should we skip the next frame?
       double frame_after_next_start_time =
diff --git a/third_party/WebKit/Source/platform/graphics/BitmapImage.h b/third_party/WebKit/Source/platform/graphics/BitmapImage.h
index 2c7af2a..c60d02b 100644
--- a/third_party/WebKit/Source/platform/graphics/BitmapImage.h
+++ b/third_party/WebKit/Source/platform/graphics/BitmapImage.h
@@ -123,7 +123,7 @@
 
   sk_sp<SkImage> FrameAtIndex(size_t);
 
-  bool FrameIsCompleteAtIndex(size_t) const;
+  bool FrameIsReceivedAtIndex(size_t) const;
   float FrameDurationAtIndex(size_t) const;
   bool FrameHasAlphaAtIndex(size_t);
   ImageOrientation FrameOrientationAtIndex(size_t);
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
index adf65ce9..d37faba7 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
@@ -46,13 +46,13 @@
   DeferredFrameData()
       : orientation_(kDefaultImageOrientation),
         duration_(0),
-        is_complete_(false),
+        is_received_(false),
         frame_bytes_(0),
         unique_id_(DecodingImageGenerator::kNeedNewImageUniqueID) {}
 
   ImageOrientation orientation_;
   float duration_;
-  bool is_complete_;
+  bool is_received_;
   size_t frame_bytes_;
   uint32_t unique_id_;
 };
@@ -219,11 +219,11 @@
   return true;
 }
 
-bool DeferredImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool DeferredImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   if (actual_decoder_)
-    return actual_decoder_->FrameIsCompleteAtIndex(index);
+    return actual_decoder_->FrameIsReceivedAtIndex(index);
   if (index < frame_data_.size())
-    return frame_data_[index].is_complete_;
+    return frame_data_[index].is_received_;
   return false;
 }
 
@@ -291,15 +291,15 @@
   for (size_t i = previous_size; i < frame_data_.size(); ++i) {
     frame_data_[i].duration_ = actual_decoder_->FrameDurationAtIndex(i);
     frame_data_[i].orientation_ = actual_decoder_->Orientation();
-    frame_data_[i].is_complete_ = actual_decoder_->FrameIsCompleteAtIndex(i);
+    frame_data_[i].is_received_ = actual_decoder_->FrameIsReceivedAtIndex(i);
   }
 
   // The last lazy decoded frame created from previous call might be
   // incomplete so update its state.
   if (previous_size) {
     const size_t last_frame = previous_size - 1;
-    frame_data_[last_frame].is_complete_ =
-        actual_decoder_->FrameIsCompleteAtIndex(last_frame);
+    frame_data_[last_frame].is_received_ =
+        actual_decoder_->FrameIsReceivedAtIndex(last_frame);
   }
 
   if (all_data_received_) {
@@ -336,7 +336,7 @@
   // We can consider decoded bitmap constant and reuse uniqueID only after all
   // data is received.  We reuse it also for multiframe images when image data
   // is partially received but the frame data is fully received.
-  if (all_data_received_ || frame_data_[index].is_complete_) {
+  if (all_data_received_ || frame_data_[index].is_received_) {
     DCHECK(frame_data_[index].unique_id_ ==
                DecodingImageGenerator::kNeedNewImageUniqueID ||
            frame_data_[index].unique_id_ == image->uniqueID());
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
index 73eafe8a..6b0f4cf 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
@@ -74,7 +74,7 @@
   int RepetitionCount() const;
   size_t ClearCacheExceptFrame(size_t index);
   bool FrameHasAlphaAtIndex(size_t index) const;
-  bool FrameIsCompleteAtIndex(size_t index) const;
+  bool FrameIsReceivedAtIndex(size_t index) const;
   float FrameDurationAtIndex(size_t index) const;
   size_t FrameBytesAtIndex(size_t index) const;
   ImageOrientation OrientationAtIndex(size_t index) const;
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
index c580df1..053a2a5 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
@@ -233,18 +233,18 @@
 TEST_F(DeferredImageDecoderTest, singleFrameImageLoading) {
   status_ = ImageFrame::kFramePartial;
   lazy_decoder_->SetData(data_, false);
-  EXPECT_FALSE(lazy_decoder_->FrameIsCompleteAtIndex(0));
+  EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
   sk_sp<SkImage> image = lazy_decoder_->CreateFrameAtIndex(0);
   ASSERT_TRUE(image);
   unsigned first_id = image->uniqueID();
-  EXPECT_FALSE(lazy_decoder_->FrameIsCompleteAtIndex(0));
+  EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
   EXPECT_TRUE(actual_decoder_);
 
   status_ = ImageFrame::kFrameComplete;
   data_->Append(" ", 1u);
   lazy_decoder_->SetData(data_, true);
   EXPECT_FALSE(actual_decoder_);
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(0));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
 
   image = lazy_decoder_->CreateFrameAtIndex(0);
   ASSERT_TRUE(image);
@@ -263,7 +263,7 @@
   sk_sp<SkImage> image = lazy_decoder_->CreateFrameAtIndex(0);
   ASSERT_TRUE(image);
   unsigned first_id = image->uniqueID();
-  EXPECT_FALSE(lazy_decoder_->FrameIsCompleteAtIndex(0));
+  EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
   EXPECT_EQ(10.0f, lazy_decoder_->FrameDurationAtIndex(0));
 
   frame_count_ = 2;
@@ -276,8 +276,8 @@
   ASSERT_TRUE(image);
   unsigned second_id = image->uniqueID();
   EXPECT_NE(first_id, second_id);
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(0));
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(1));
   EXPECT_EQ(20.0f, lazy_decoder_->FrameDurationAtIndex(1));
   EXPECT_TRUE(actual_decoder_);
 
@@ -286,9 +286,9 @@
   status_ = ImageFrame::kFrameComplete;
   lazy_decoder_->SetData(data_, true);
   EXPECT_FALSE(actual_decoder_);
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(0));
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(1));
-  EXPECT_TRUE(lazy_decoder_->FrameIsCompleteAtIndex(2));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(1));
+  EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(2));
   EXPECT_EQ(10.0f, lazy_decoder_->FrameDurationAtIndex(0));
   EXPECT_EQ(20.0f, lazy_decoder_->FrameDurationAtIndex(1));
   EXPECT_EQ(30.0f, lazy_decoder_->FrameDurationAtIndex(2));
diff --git a/third_party/WebKit/Source/platform/graphics/ImageSource.cpp b/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
index 38c5eff0..a10c7b71 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
@@ -141,8 +141,8 @@
   return !decoder_ || decoder_->FrameHasAlphaAtIndex(index);
 }
 
-bool ImageSource::FrameIsCompleteAtIndex(size_t index) const {
-  return decoder_ && decoder_->FrameIsCompleteAtIndex(index);
+bool ImageSource::FrameIsReceivedAtIndex(size_t index) const {
+  return decoder_ && decoder_->FrameIsReceivedAtIndex(index);
 }
 
 size_t ImageSource::FrameBytesAtIndex(size_t index) const {
diff --git a/third_party/WebKit/Source/platform/graphics/ImageSource.h b/third_party/WebKit/Source/platform/graphics/ImageSource.h
index a6a9eac2..9432054aa 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageSource.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageSource.h
@@ -96,7 +96,7 @@
   float FrameDurationAtIndex(size_t) const;
   bool FrameHasAlphaAtIndex(
       size_t) const;  // Whether or not the frame actually used any alpha.
-  bool FrameIsCompleteAtIndex(
+  bool FrameIsReceivedAtIndex(
       size_t) const;  // Whether or not the frame is fully received.
   ImageOrientation OrientationAtIndex(size_t) const;  // EXIF image orientation
 
diff --git a/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h b/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
index 74fe6ff..7fd410f 100644
--- a/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
+++ b/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
@@ -83,7 +83,7 @@
 
   int RepetitionCount() const override { return client_->RepetitionCount(); }
 
-  bool FrameIsCompleteAtIndex(size_t) const override {
+  bool FrameIsReceivedAtIndex(size_t) const override {
     return client_->GetStatus() == ImageFrame::kFrameComplete;
   }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index e9f93aec..9f070089 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -150,11 +150,11 @@
 }
 
 bool ImageDecoder::FrameHasAlphaAtIndex(size_t index) const {
-  return !FrameIsCompleteAtIndex(index) ||
+  return !FrameIsReceivedAtIndex(index) ||
          frame_buffer_cache_[index].HasAlpha();
 }
 
-bool ImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool ImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   // Animated images override this method to return the status based on the data
   // received for the queried frame.
   return IsAllDataReceived();
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
index 96e6159..36d8027 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
@@ -191,7 +191,7 @@
   virtual bool FrameHasAlphaAtIndex(size_t) const;
 
   // Whether or not the frame is fully received.
-  virtual bool FrameIsCompleteAtIndex(size_t) const;
+  virtual bool FrameIsReceivedAtIndex(size_t) const;
 
   // Returns true if a cached complete decode is available.
   bool FrameIsDecodedAtIndex(size_t) const;
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
index b954a98..dc4823d 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -79,7 +79,7 @@
   return repetition_count_;
 }
 
-bool GIFImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool GIFImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   return reader_ && (index < reader_->ImagesCount()) &&
          reader_->FrameContext(index)->IsComplete();
 }
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
index dec862c..54069d7b 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
@@ -50,7 +50,7 @@
   String FilenameExtension() const override { return "gif"; }
   void OnSetData(SegmentReader* data) override;
   int RepetitionCount() const override;
-  bool FrameIsCompleteAtIndex(size_t) const override;
+  bool FrameIsReceivedAtIndex(size_t) const override;
   float FrameDurationAtIndex(size_t) const override;
   // CAUTION: SetFailed() deletes |reader_|.  Be careful to avoid
   // accessing deleted memory, especially when calling this from inside
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
index 05e56e9f..8bc212a2 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
@@ -197,8 +197,8 @@
 
   EXPECT_EQ(2u, decoder->FrameCount());
   EXPECT_FALSE(decoder->Failed());
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
   EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
 }
 
@@ -217,13 +217,13 @@
 
   EXPECT_EQ(2u, decoder->FrameCount());
   EXPECT_FALSE(decoder->Failed());
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
-  EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+  EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(1));
 
   decoder->SetData(data_buffer.Get(), true);
   EXPECT_EQ(2u, decoder->FrameCount());
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
 }
 
 TEST(GIFImageDecoderTest, badTerminator) {
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
index 65b2dc1..357ceea 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
@@ -82,7 +82,7 @@
              : ((IntSize(width, height) == frame_size_) || SetFailed());
 }
 
-bool ICOImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool ICOImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   if (index >= dir_entries_.size())
     return false;
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
index d42a53a..a6ceb69 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
@@ -53,7 +53,7 @@
   IntSize Size() const override;
   IntSize FrameSizeAtIndex(size_t) const override;
   bool SetSize(unsigned width, unsigned height) override;
-  bool FrameIsCompleteAtIndex(size_t) const override;
+  bool FrameIsReceivedAtIndex(size_t) const override;
   // CAUTION: SetFailed() deletes all readers and decoders.  Be careful to
   // avoid accessing deleted memory, especially when calling this from
   // inside BMPImageReader!
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
index 0e12da8..44ee333 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
@@ -468,17 +468,16 @@
   buffer.SetStatus(ImageFrame::kFrameComplete);
 }
 
-bool PNGImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool PNGImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   if (!IsDecodedSizeAvailable())
     return false;
 
   DCHECK(!Failed() && reader_);
 
-  // For non-animated images, return whether the status of the frame is
-  // ImageFrame::FrameComplete with ImageDecoder::FrameIsCompleteAtIndex.
+  // For non-animated images, return ImageDecoder::FrameIsReceivedAtIndex.
   // This matches the behavior of WEBPImageDecoder.
   if (reader_->ParseCompleted() && reader_->FrameCount() == 1)
-    return ImageDecoder::FrameIsCompleteAtIndex(index);
+    return ImageDecoder::FrameIsReceivedAtIndex(index);
 
   return reader_->FrameIsReceivedAtIndex(index);
 }
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
index 8e30a3e..3075bfa1 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
@@ -46,7 +46,7 @@
   String FilenameExtension() const override { return "png"; }
   bool SetSize(unsigned, unsigned) override;
   int RepetitionCount() const override;
-  bool FrameIsCompleteAtIndex(size_t) const override;
+  bool FrameIsReceivedAtIndex(size_t) const override;
   float FrameDurationAtIndex(size_t) const override;
   bool SetFailed() override;
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
index e99ca52..dfdacf58 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
@@ -1007,21 +1007,21 @@
         SharedBuffer::Create(full_data->Data(), rec.offset_in_first_frame);
     decoder->SetData(data.Get(), false);
 
-    EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(0));
+    EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
 
     // Parsing the size is not enough to mark the frame as complete.
     EXPECT_TRUE(decoder->IsSizeAvailable());
-    EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(0));
+    EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
 
     const auto partial_frame_count = decoder->FrameCount();
     EXPECT_EQ(1u, partial_frame_count);
 
     // Frame is not complete, even after decoding partially.
-    EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(0));
+    EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
     auto* frame = decoder->FrameBufferAtIndex(0);
     ASSERT_TRUE(frame);
     EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus());
-    EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(0));
+    EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
 
     decoder->SetData(full_data.Get(), true);
 
@@ -1029,21 +1029,21 @@
     // complete for animated images.
     EXPECT_TRUE(decoder->IsSizeAvailable());
     if (rec.expected_frame_count > 1)
-      EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(0));
+      EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
     else
-      EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
+      EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
 
     const auto frame_count = decoder->FrameCount();
     ASSERT_EQ(rec.expected_frame_count, frame_count);
 
     // After parsing (the full file), all frames are complete.
     for (size_t i = 0; i < frame_count; ++i)
-      EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(i));
+      EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(i));
 
     frame = decoder->FrameBufferAtIndex(0);
     ASSERT_TRUE(frame);
     EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
-    EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
+    EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
   }
 }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
index 06aee19..1d4fad0 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
@@ -162,11 +162,11 @@
   return Failed() ? kAnimationLoopOnce : repetition_count_;
 }
 
-bool WEBPImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
+bool WEBPImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
   if (!demux_ || demux_state_ <= WEBP_DEMUX_PARSING_HEADER)
     return false;
   if (!(format_flags_ & ANIMATION_FLAG))
-    return ImageDecoder::FrameIsCompleteAtIndex(index);
+    return ImageDecoder::FrameIsReceivedAtIndex(index);
   bool frame_is_received_at_index = index < frame_buffer_cache_.size();
   return frame_is_received_at_index;
 }
@@ -514,7 +514,7 @@
       ClearDecoder();
       return true;
     case VP8_STATUS_SUSPENDED:
-      if (!IsAllDataReceived() && !FrameIsCompleteAtIndex(frame_index)) {
+      if (!IsAllDataReceived() && !FrameIsReceivedAtIndex(frame_index)) {
         ApplyPostProcessing(frame_index);
         return false;
       }
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
index ba2bd3d1..68d6bc5 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
@@ -48,7 +48,7 @@
   String FilenameExtension() const override { return "webp"; }
   void OnSetData(SegmentReader* data) override;
   int RepetitionCount() const override;
-  bool FrameIsCompleteAtIndex(size_t) const override;
+  bool FrameIsReceivedAtIndex(size_t) const override;
   float FrameDurationAtIndex(size_t) const override;
 
  private:
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
index 4ba3df7..30eb7daf 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
@@ -352,18 +352,18 @@
 
   EXPECT_EQ(2u, decoder->FrameCount());
   EXPECT_FALSE(decoder->Failed());
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
   EXPECT_EQ(1000, decoder->FrameDurationAtIndex(0));
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
   EXPECT_EQ(500, decoder->FrameDurationAtIndex(1));
 
   decoder->SetData(data_buffer.Get(), true);
   EXPECT_EQ(3u, decoder->FrameCount());
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
   EXPECT_EQ(1000, decoder->FrameDurationAtIndex(0));
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
   EXPECT_EQ(500, decoder->FrameDurationAtIndex(1));
-  EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(2));
+  EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(2));
   EXPECT_EQ(1000.0, decoder->FrameDurationAtIndex(2));
 }
 
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.cpp b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.cpp
index a3aa1140..e4ec3f8 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.cpp
@@ -16,12 +16,8 @@
 
 void TextCodecReplacement::RegisterEncodingNames(
     EncodingNameRegistrar registrar) {
-  // The 'replacement' label itself should not be referenceable by
-  // resources or script - it's a specification convenience - but much of
-  // the wtf/text API asserts that an encoding name is a label for itself.
-  // This is handled in TextEncoding by marking it as not valid.
+  // Taken from the alias table at·https://encoding.spec.whatwg.org/
   registrar("replacement", "replacement");
-
   registrar("csiso2022kr", "replacement");
   registrar("hz-gb-2312", "replacement");
   registrar("iso-2022-cn", "replacement");
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.h b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.h
index c03167f..f6862056 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.h
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacement.h
@@ -10,6 +10,11 @@
 
 namespace WTF {
 
+// The "replacement" encoding exists to prevent attacks that abuse a mismatch
+// between encodings supported on the server and the client. The encoder is
+// the same as UTF-8; and for a non-empty input the decoder emits U+FFFD and
+// terminates. See: https://encoding.spec.whatwg.org/#replacement and
+// https://encoding.spec.whatwg.org/#output-encodings
 class TextCodecReplacement final : public TextCodecUTF8 {
  public:
   TextCodecReplacement();
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacementTest.cpp b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacementTest.cpp
index de1d0d6..3d45c47b 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacementTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecReplacementTest.cpp
@@ -20,9 +20,8 @@
 const char* g_replacement_alias = "iso-2022-kr";
 
 TEST(TextCodecReplacement, Aliases) {
-  // "replacement" is not a valid alias for itself
-  EXPECT_FALSE(TextEncoding("replacement").IsValid());
-  EXPECT_FALSE(TextEncoding("rEpLaCeMeNt").IsValid());
+  EXPECT_TRUE(TextEncoding("replacement").IsValid());
+  EXPECT_TRUE(TextEncoding("rEpLaCeMeNt").IsValid());
 
   EXPECT_TRUE(TextEncoding(g_replacement_alias).IsValid());
   EXPECT_STREQ("replacement", TextEncoding(g_replacement_alias).GetName());
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextEncoding.cpp b/third_party/WebKit/Source/platform/wtf/text/TextEncoding.cpp
index 95fd28f..680c678 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextEncoding.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextEncoding.cpp
@@ -44,16 +44,10 @@
 
 TextEncoding::TextEncoding(const char* name)
     : name_(AtomicCanonicalTextEncodingName(name)) {
-  // Aliases are valid, but not "replacement" itself.
-  if (name_ && IsReplacementEncoding(name))
-    name_ = 0;
 }
 
 TextEncoding::TextEncoding(const String& name)
     : name_(AtomicCanonicalTextEncodingName(name)) {
-  // Aliases are valid, but not "replacement" itself.
-  if (name_ && IsReplacementEncoding(name))
-    name_ = 0;
 }
 
 String TextEncoding::Decode(const char* data,
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.cpp b/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.cpp
index b0709fb..7e4c1339 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.cpp
@@ -222,14 +222,6 @@
   TextCodecUserDefined::RegisterCodecs(AddToTextCodecMap);
 }
 
-bool IsReplacementEncoding(const char* alias) {
-  return alias && !strcasecmp(alias, "replacement");
-}
-
-bool IsReplacementEncoding(const String& alias) {
-  return alias == "replacement";
-}
-
 static void ExtendTextCodecMaps() {
   TextCodecReplacement::RegisterEncodingNames(AddToTextEncodingNameMap);
   TextCodecReplacement::RegisterCodecs(AddToTextCodecMap);
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.h b/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.h
index e92fcc3..1a715dc 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.h
+++ b/third_party/WebKit/Source/platform/wtf/text/TextEncodingRegistry.h
@@ -46,8 +46,6 @@
 const char* AtomicCanonicalTextEncodingName(const CharacterType*, size_t);
 const char* AtomicCanonicalTextEncodingName(const String&);
 bool NoExtendedTextEncodingNameUsed();
-bool IsReplacementEncoding(const char* alias);
-bool IsReplacementEncoding(const String& alias);
 
 #ifndef NDEBUG
 void DumpTextEncodingNameMap();
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
index 1edcba9fd..e3b5857 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
@@ -145,7 +145,22 @@
         """
         fs = self._filesystem
         output_filename = fs.join(self._root_output_dir, self._test_name)
-        return fs.splitext(output_filename)[0] + modifier
+        base, extension = fs.splitext(output_filename)
+
+        # Below is an affordance for WPT test files that become multiple tests using different URL params,
+        # For example,
+        # - html/syntax/parsing/html5lib_adoption01.html
+        # Becomes two tests:
+        # - html/syntax/parsing/html5lib_adoption01.html?run_type=write
+        # - html/syntax/parsing/html5lib_adoption01.html?run_type=uri
+        # But previously their result file would be the same, since everything after the extension
+        # is removed. Instead, for files with URL params, we use the whole filename for writing results.
+        if '?' in extension:
+            # Question marks are reserved characters in Windows filenames.
+            sanitized_filename = output_filename.replace('?', '_')
+            return sanitized_filename + modifier
+
+        return base + modifier
 
     def _write_file(self, path, contents):
         if contents is not None:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations.py
index ca7dfe15..c357dfd 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations.py
@@ -35,6 +35,7 @@
 from webkitpy.common import exit_codes
 from webkitpy.layout_tests.models import test_expectations
 from webkitpy.layout_tests.port.factory import platform_options
+from webkitpy.w3c.wpt_manifest import WPTManifest
 
 _log = logging.getLogger(__name__)
 
@@ -151,6 +152,11 @@
     else:
         host = Host()
 
+    # Need to generate MANIFEST.json since some expectations correspond to WPT
+    # tests that aren't files and only exist in the manifest.
+    _log.info('Generating MANIFEST.json for web-platform-tests ...')
+    WPTManifest.ensure_manifest(host)
+
     try:
         exit_status = run_checks(host, options, stderr)
     except KeyboardInterrupt:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 93ec408..1fab56b7 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -34,7 +34,6 @@
 
 import collections
 import errno
-import functools
 import json
 import logging
 import optparse
@@ -667,22 +666,43 @@
         return reftest_list
 
     def tests(self, paths):
-        """Returns the list of tests found matching paths."""
+        """Returns all tests or tests matching supplied paths.
+
+        Args:
+            paths: Array of paths to match. If supplied, this function will only
+                return tests matching at least one path in paths.
+
+        Returns:
+            An array of test paths and test names. The latter are web platform
+            tests that don't correspond to file paths but are valid tests,
+            for instance a file path test.any.js could correspond to two test
+            names: test.any.html and test.any.worker.html.
+        """
         tests = self.real_tests(paths)
 
         suites = self.virtual_test_suites()
         if paths:
             tests.extend(self._virtual_tests_matching_paths(paths, suites))
+            tests.extend(self._wpt_test_urls_matching_paths(paths))
         else:
             tests.extend(self._all_virtual_tests(suites))
+            tests.extend(['external/wpt' + test for test in self._wpt_manifest().all_urls()])
+
         return tests
 
     def real_tests(self, paths):
         # When collecting test cases, skip these directories.
-        skipped_directories = set(['platform', 'resources', 'support', 'script-tests', 'reference', 'reftest'])
-        files = find_files.find(self._filesystem, self.layout_tests_dir(), paths,
-                                skipped_directories, functools.partial(Port.is_test_file, self), self.test_key)
-        return self._convert_wpt_file_paths_to_url_paths([self.relative_test_filename(f) for f in files])
+        skipped_directories = set([
+            'platform', 'resources', 'support', 'script-tests',
+            'reference', 'reftest', 'external'
+        ])
+        is_non_wpt_real_test_file = lambda fs, dirname, filename: (
+            self.is_test_file(fs, dirname, filename)
+            and not re.search(r'[/\\]external[/\\]wpt([/\\].*)?$', dirname)
+        )
+        files = find_files.find(self._filesystem, self.layout_tests_dir(), paths, skipped_directories,
+                                is_non_wpt_real_test_file, self.test_key)
+        return [self.relative_test_filename(f) for f in files]
 
     @staticmethod
     # If any changes are made here be sure to update the isUsedInReftest method in old-run-webkit-tests as well.
@@ -722,17 +742,15 @@
         return Port._has_supported_extension(
             filesystem, filename) and not Port.is_reference_html_file(filesystem, dirname, filename)
 
-    def _convert_wpt_file_paths_to_url_paths(self, files):
+    def _convert_wpt_file_path_to_url_paths(self, file_path):
         tests = []
-        for file_path in files:
-            # Path separators are normalized by relative_test_filename().
-            match = re.search(r'external/wpt/(.*)$', file_path)
-            if not match:
-                tests.append(file_path)
-                continue
-            urls = self._wpt_manifest().file_path_to_url_paths(match.group(1))
-            for url in urls:
-                tests.append(file_path[0:match.start(1)] + url)
+        # Path separators are normalized by relative_test_filename().
+        match = re.search(r'external/wpt/(.*)$', file_path)
+        if not match:
+            return [file_path]
+        urls = self._wpt_manifest().file_path_to_url_paths(match.group(1))
+        for url in urls:
+            tests.append(file_path[0:match.start(1)] + url)
         return tests
 
     @memoized
@@ -828,7 +846,7 @@
         """Returns True if the test name refers to an existing test or baseline."""
         # Used by test_expectations.py to determine if an entry refers to a
         # valid test and by printing.py to determine if baselines exist.
-        return self.test_isfile(test_name) or self.test_isdir(test_name)
+        return self.is_wpt_test(test_name) or self.test_isfile(test_name) or self.test_isdir(test_name)
 
     def split_test(self, test_name):
         """Splits a test name into the 'directory' part and the 'basename' part."""
@@ -1518,9 +1536,42 @@
                     tests.append(test)
         return tests
 
+    def _wpt_test_urls_matching_paths(self, paths):
+        tests = []
+
+        for test_url_path in self._wpt_manifest().all_urls():
+            if test_url_path[0] == '/':
+                test_url_path = test_url_path[1:]
+
+            full_test_url_path = 'external/wpt/' + test_url_path
+
+            for path in paths:
+                if 'external' not in path:
+                    continue
+
+                wpt_path = path.replace('external/wpt/', '')
+
+                # When `test_url_path` is test.any.html or test.any.worker.html and path is test.any.js
+                matches_any_js_test = (
+                    self._wpt_manifest().is_test_file(wpt_path)
+                    and test_url_path.startswith(re.sub(r'\.js$', '', wpt_path))
+                )
+
+                # Get a list of directories for both paths, filter empty strings
+                full_test_url_directories = filter(None, full_test_url_path.split(self._filesystem.sep))
+                path_directories = filter(None, path.split(self._filesystem.sep))
+
+                # For all other path matches within WPT
+                if matches_any_js_test or path_directories == full_test_url_directories[0:len(path_directories)]:
+                    wpt_file_paths = self._convert_wpt_file_path_to_url_paths(test_url_path)
+                    tests.extend('external/wpt/' + wpt_file_path for wpt_file_path in wpt_file_paths)
+
+        return tests
+
     def _populate_virtual_suite(self, suite):
         if not suite.tests:
             base_tests = self.real_tests([suite.base])
+            base_tests.extend(self._wpt_test_urls_matching_paths([suite.base]))
             suite.tests = {}
             for test in base_tests:
                 suite.tests[test.replace(suite.base, suite.name, 1)] = test
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 28827c8..522643e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -281,14 +281,72 @@
         PortTest._add_manifest_to_mock_file_system(port.host.filesystem)
         self.assertIn('external/wpt/dom/ranges/Range-attributes.html', port.tests([]))
         self.assertNotIn('external/wpt/console/console-is-a-namespace.any.js', port.tests([]))
-        self.assertEqual(port.tests(['external']), ['external/wpt/dom/ranges/Range-attributes.html'])
-        self.assertEqual(port.tests(['external/']), ['external/wpt/dom/ranges/Range-attributes.html'])
+
+        # test.any.js shows up on the filesystem as one file but it effectively becomes two test files:
+        # test.any.html and test.any.worker.html. We should support running test.any.js by name and
+        # indirectly by specifying a parent directory.
+        self.assertEqual(port.tests(['external']),
+                         ['external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html',
+                          'external/wpt/dom/ranges/Range-attributes.html',
+                          'external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/']),
+                         ['external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html',
+                          'external/wpt/dom/ranges/Range-attributes.html',
+                          'external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
         self.assertEqual(port.tests(['external/csswg-test']), [])
-        self.assertEqual(port.tests(['external/wpt']), ['external/wpt/dom/ranges/Range-attributes.html'])
-        self.assertEqual(port.tests(['external/wpt/']), ['external/wpt/dom/ranges/Range-attributes.html'])
+        self.assertEqual(port.tests(['external/wpt']),
+                         ['external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html',
+                          'external/wpt/dom/ranges/Range-attributes.html',
+                          'external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/']),
+                         ['external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html',
+                          'external/wpt/dom/ranges/Range-attributes.html',
+                          'external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/console']),
+                         ['external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/console/']),
+                         ['external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/console/console-is-a-namespace.any.js']),
+                         ['external/wpt/console/console-is-a-namespace.any.worker.html',
+                          'external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/console/console-is-a-namespace.any.html']),
+                         ['external/wpt/console/console-is-a-namespace.any.html'])
+        self.assertEqual(port.tests(['external/wpt/dom']),
+                         ['external/wpt/dom/ranges/Range-attributes.html'])
+        self.assertEqual(port.tests(['external/wpt/dom/']),
+                         ['external/wpt/dom/ranges/Range-attributes.html'])
         self.assertEqual(port.tests(['external/wpt/dom/ranges/Range-attributes.html']),
                          ['external/wpt/dom/ranges/Range-attributes.html'])
 
+    def test_virtual_wpt_tests_paths(self):
+        port = self.make_port(with_tests=True)
+        PortTest._add_manifest_to_mock_file_system(port.host.filesystem)
+        all_wpt = [
+            'virtual/virtual_wpt/external/wpt/console/console-is-a-namespace.any.html',
+            'virtual/virtual_wpt/external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html',
+            'virtual/virtual_wpt/external/wpt/console/console-is-a-namespace.any.worker.html',
+            'virtual/virtual_wpt/external/wpt/dom/ranges/Range-attributes.html',
+        ]
+        dom_wpt = [
+            'virtual/virtual_wpt_dom/external/wpt/dom/ranges/Range-attributes.html',
+        ]
+
+        self.assertEqual(port.tests(['virtual/virtual_wpt/external/']), all_wpt)
+        self.assertEqual(port.tests(['virtual/virtual_wpt/external/wpt/']), all_wpt)
+        self.assertEqual(port.tests(['virtual/virtual_wpt/external/wpt/console']),
+                         ['virtual/virtual_wpt/external/wpt/console/console-is-a-namespace.any.html',
+                          'virtual/virtual_wpt/external/wpt/console/console-is-a-namespace.any.worker.html'])
+
+        self.assertEqual(port.tests(['virtual/virtual_wpt_dom/external/wpt/dom/']), dom_wpt)
+        self.assertEqual(port.tests(['virtual/virtual_wpt_dom/external/wpt/dom/ranges/']), dom_wpt)
+        self.assertEqual(port.tests(['virtual/virtual_wpt_dom/external/wpt/dom/ranges/Range-attributes.html']), dom_wpt)
+
     def test_is_test_file(self):
         port = self.make_port(with_tests=True)
         is_test_file = functools.partial(Port.is_test_file, port, port.host.filesystem)
@@ -329,6 +387,16 @@
         # A file in external/wpt_automation.
         self.assertTrue(port.is_test_file(filesystem, LAYOUT_TEST_DIR + '/external/wpt_automation', 'foo.html'))
 
+    def test_is_wpt_test(self):
+        port = self.make_port(with_tests=True)
+        filesystem = port.host.filesystem
+        PortTest._add_manifest_to_mock_file_system(filesystem)
+
+        self.assertTrue(port.is_wpt_test('external/wpt/dom/ranges/Range-attributes.html'))
+        self.assertTrue(port.is_wpt_test('external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html'))
+        self.assertFalse(port.is_wpt_test('dom/domparsing/namespaces-1.html'))
+        self.assertFalse(port.is_wpt_test('rutabaga'))
+
     def test_is_slow_wpt_test(self):
         port = self.make_port(with_tests=True)
         filesystem = port.host.filesystem
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
index e133fdb..40774ab3 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
@@ -533,6 +533,8 @@
             VirtualTestSuite(prefix='skipped', base='failures/expected', args=['--virtual-arg2']),
             VirtualTestSuite(prefix='references_use_default_args', base='passes/reftest.html',
                              args=['--virtual-arg'], references_use_default_args=True),
+            VirtualTestSuite(prefix='virtual_wpt', base='external/wpt', args=['--virtual-arg']),
+            VirtualTestSuite(prefix='virtual_wpt_dom', base='external/wpt/dom', args=['--virtual-arg']),
         ]
 
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
index d7eac77..a68714d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
@@ -101,6 +101,8 @@
                 self.mac_port.layout_tests_dir(), test)
             self._write(path, 'contents')
 
+        self.mac_port.host.filesystem.write_text_file('/test.checkout/LayoutTests/external/wpt/MANIFEST.json', '{}')
+
     def tearDown(self):
         BaseTestCase.tearDown(self)
         LoggingTestCase.tearDown(self)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
index 25be40d..128a038 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
@@ -13,6 +13,7 @@
 import logging
 
 from webkitpy.common.path_finder import PathFinder
+from webkitpy.common.memoized import memoized
 
 _log = logging.getLogger(__file__)
 
@@ -23,6 +24,7 @@
         # TODO(tkent): Create a Manifest object by Manifest.from_json().
         # See ../thirdparty/wpt/wpt/tools/manifest/manifest.py.
         self.raw_dict = json.loads(json_content)
+        self.test_types = ('manual', 'reftest', 'testharness')
 
     def _items_for_path(self, path_in_wpt):
         """Returns manifest items for the given WPT path, or None if not found.
@@ -35,28 +37,50 @@
         it will be a list with three items ([url, references, extras]).
         """
         items = self.raw_dict['items']
-        if path_in_wpt in items['manual']:
-            return items['manual'][path_in_wpt]
-        elif path_in_wpt in items['reftest']:
-            return items['reftest'][path_in_wpt]
-        elif path_in_wpt in items['testharness']:
-            return items['testharness'][path_in_wpt]
+        for test_type in self.test_types:
+            if path_in_wpt in items[test_type]:
+                return items[test_type][path_in_wpt]
         return None
 
+    @memoized
+    def all_urls(self):
+        """Returns a set of the urls for all items in the manifest."""
+        urls = set()
+        if 'items' in self.raw_dict:
+            items = self.raw_dict['items']
+            for category in self.test_types:
+                if category in items:
+                    for records in items[category].values():
+                        urls.update([item[0] for item in records])
+        return urls
+
+    @memoized
+    def all_wpt_tests(self):
+        if 'items' not in self.raw_dict:
+            return []
+
+        all_tests = []
+        for test_type in self.test_types:
+            for path_in_wpt in self.raw_dict['items'][test_type]:
+                all_tests.append(path_in_wpt)
+        return all_tests
+
     def is_test_file(self, path_in_wpt):
         return self._items_for_path(path_in_wpt) is not None
 
+    def is_test_url(self, url):
+        """Checks if url is a valid test in the manifest.
+
+        The url must be the WPT test name with a leading slash (/).
+        """
+        if url[0] != '/':
+            raise Exception('Test url missing leading /: %s' % url)
+        return url in self.all_urls()
+
     def file_path_to_url_paths(self, path_in_wpt):
         manifest_items = self._items_for_path(path_in_wpt)
         assert manifest_items is not None
-        if len(manifest_items) != 1:
-            return []
-        url = manifest_items[0][0]
-        if url[1:] != path_in_wpt:
-            # TODO(tkent): foo.any.js and bar.worker.js should be accessed
-            # as foo.any.html, foo.any.worker, and bar.worker with WPTServe.
-            return []
-        return [path_in_wpt]
+        return [item[0][1:] for item in manifest_items]
 
     @staticmethod
     def _get_extras_from_item(item):
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c
index f0075cfb..68fd2604 100644
--- a/third_party/sqlite/amalgamation/sqlite3.c
+++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -4065,6 +4065,15 @@
 ** [sqlite3_blob_open | incremental BLOB I/O] routines.
 ** ^A negative value for the zeroblob results in a zero-length BLOB.
 **
+** ^The sqlite3_bind_pointer(S,I,P) routine causes the I-th parameter in
+** [prepared statement] S to have an SQL value of NULL, but to also be
+** associated with the pointer P.
+** ^The sqlite3_bind_pointer() routine can be used to pass
+** host-language pointers into [application-defined SQL functions].
+** ^A parameter that is initialized using [sqlite3_bind_pointer()] appears
+** to be an ordinary SQL NULL value to everything other than
+** [sqlite3_value_pointer()].
+**
 ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
 ** for the [prepared statement] or with a prepared statement for which
 ** [sqlite3_step()] has been called more recently than [sqlite3_reset()],
@@ -4098,6 +4107,7 @@
 SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
                          void(*)(void*), unsigned char encoding);
 SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+SQLITE_API int sqlite3_bind_pointer(sqlite3_stmt*, int, void*);
 SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
 SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
 
@@ -4867,6 +4877,11 @@
 ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
 ** extract UTF-16 strings as big-endian and little-endian respectively.
 **
+** ^If [sqlite3_value] object V was initialized
+** using [sqlite3_bind_pointer(S,I,P)] or [sqlite3_result_pointer(C,P)], then
+** sqlite3_value_pointer(V) will return the pointer P.  Otherwise,
+** sqlite3_value_pointer(V) returns a NULL.
+**
 ** ^(The sqlite3_value_numeric_type() interface attempts to apply
 ** numeric affinity to the value.  This means that an attempt is
 ** made to convert the value to an integer or floating point.  If
@@ -4894,6 +4909,7 @@
 SQLITE_API const void *sqlite3_value_text16(sqlite3_value*);
 SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*);
 SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);
+SQLITE_API void *sqlite3_value_pointer(sqlite3_value*);
 SQLITE_API int sqlite3_value_type(sqlite3_value*);
 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
 
@@ -4906,10 +4922,6 @@
 ** information can be used to pass a limited amount of context from
 ** one SQL function to another.  Use the [sqlite3_result_subtype()]
 ** routine to set the subtype for the return value of an SQL function.
-**
-** SQLite makes no use of subtype itself.  It merely passes the subtype
-** from the result of one [application-defined SQL function] into the
-** input of another.
 */
 SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
 
@@ -5187,6 +5199,14 @@
 ** [unprotected sqlite3_value] object is required, so either
 ** kind of [sqlite3_value] object can be used with this interface.
 **
+** ^The sqlite3_result_pointer(C,P) interface sets the result to an
+** SQL NULL value, just like [sqlite3_result_null(C)], except that it
+** also associates the host-language pointer P with that NULL value such
+** that the pointer can be retrieved within an
+** [application-defined SQL function] using [sqlite3_value_pointer()].
+** This mechanism can be used to pass non-SQL values between
+** application-defined functions.
+**
 ** If these routines are called from within the different thread
 ** than the one containing the application-defined function that received
 ** the [sqlite3_context] pointer, the results are undefined.
@@ -5210,6 +5230,7 @@
 SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
 SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
 SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+SQLITE_API void sqlite3_result_pointer(sqlite3_context*, void*);
 SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);
 SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
 
@@ -18089,6 +18110,7 @@
     double r;           /* Real value used when MEM_Real is set in flags */
     i64 i;              /* Integer value used when MEM_Int is set in flags */
     int nZero;          /* Used when bit MEM_Zero is set in flags */
+    void *pPtr;         /* Pointer when flags=MEM_NULL and eSubtype='p' */
     FuncDef *pDef;      /* Used only when flags==MEM_Agg */
     RowSet *pRowSet;    /* Used only when flags==MEM_RowSet */
     VdbeFrame *pFrame;  /* Used when flags==MEM_Frame */
@@ -18374,6 +18396,7 @@
 #else
 SQLITE_PRIVATE   void sqlite3VdbeMemSetDouble(Mem*, double);
 #endif
+SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem*, void*);
 SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
 SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
 SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
@@ -70280,6 +70303,17 @@
   }
 }
 
+/*
+** Set the value stored in *pMem should already be a NULL.
+** Also store a pointer to go with it.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem *pMem, void *pPtr){
+  assert( pMem->flags==MEM_Null );
+  pMem->flags = MEM_Null|MEM_Subtype;
+  pMem->u.pPtr = pPtr;
+  pMem->eSubtype = 'p';
+}
+
 #ifndef SQLITE_OMIT_FLOATING_POINT
 /*
 ** Delete any previous value and set the value stored in *pMem to val,
@@ -76159,6 +76193,14 @@
   Mem *pMem = (Mem*)pVal;
   return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
 }
+SQLITE_API void *sqlite3_value_pointer(sqlite3_value *pVal){
+  Mem *p = (Mem*)pVal;
+  if( (p->flags & MEM_TypeMask)==(MEM_Null|MEM_Subtype) && p->eSubtype=='p' ){
+    return p->u.pPtr;
+  }else{
+    return 0;
+  }
+}
 SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
   return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
 }
@@ -76337,6 +76379,12 @@
   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
   sqlite3VdbeMemSetNull(pCtx->pOut);
 }
+SQLITE_API void sqlite3_result_pointer(sqlite3_context *pCtx, void *pPtr){
+  Mem *pOut = pCtx->pOut;
+  assert( sqlite3_mutex_held(pOut->db->mutex) );
+  sqlite3VdbeMemSetNull(pOut);
+  sqlite3VdbeMemSetPointer(pOut, pPtr);
+}
 SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
   Mem *pOut = pCtx->pOut;
   assert( sqlite3_mutex_held(pOut->db->mutex) );
@@ -77322,6 +77370,16 @@
   }
   return rc;
 }
+SQLITE_API int sqlite3_bind_pointer(sqlite3_stmt *pStmt, int i, void *pPtr){
+  int rc;
+  Vdbe *p = (Vdbe*)pStmt;
+  rc = vdbeUnbind(p, i);
+  if( rc==SQLITE_OK ){
+    sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr);
+    sqlite3_mutex_leave(p->db->mutex);
+  }
+  return rc;
+}
 SQLITE_API int sqlite3_bind_text( 
   sqlite3_stmt *pStmt, 
   int i, 
@@ -148237,9 +148295,8 @@
     sqlite3_result_int64(pCtx, pCsr->iPrevId);
   }else if( iCol==p->nColumn ){
     /* The extra column whose name is the same as the table.
-    ** Return a blob which is a pointer to the cursor.  */
-    sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
-    sqlite3_result_subtype(pCtx, '3');
+    ** Return a pointer to the cursor.  */
+    sqlite3_result_pointer(pCtx, pCsr);
   }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
     sqlite3_result_int64(pCtx, pCsr->iLangid);
   }else{
@@ -148451,17 +148508,13 @@
   sqlite3_value *pVal,            /* argv[0] passed to function */
   Fts3Cursor **ppCsr              /* OUT: Store cursor handle here */
 ){
-  Fts3Cursor *pRet;
-  if( sqlite3_value_type(pVal)!=SQLITE_BLOB 
-   || sqlite3_value_subtype(pVal)!='3'
-   || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
-  ){
+  Fts3Cursor *pRet = (Fts3Cursor*)sqlite3_value_pointer(pVal);
+  if( pRet==0 ){
     char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
     sqlite3_result_error(pContext, zErr, -1);
     sqlite3_free(zErr);
     return SQLITE_ERROR;
   }
-  memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
   *ppCsr = pRet;
   return SQLITE_OK;
 }
diff --git a/third_party/sqlite/amalgamation/sqlite3.h b/third_party/sqlite/amalgamation/sqlite3.h
index 8e4e11e..a476a98 100644
--- a/third_party/sqlite/amalgamation/sqlite3.h
+++ b/third_party/sqlite/amalgamation/sqlite3.h
@@ -3786,6 +3786,15 @@
 ** [sqlite3_blob_open | incremental BLOB I/O] routines.
 ** ^A negative value for the zeroblob results in a zero-length BLOB.
 **
+** ^The sqlite3_bind_pointer(S,I,P) routine causes the I-th parameter in
+** [prepared statement] S to have an SQL value of NULL, but to also be
+** associated with the pointer P.
+** ^The sqlite3_bind_pointer() routine can be used to pass
+** host-language pointers into [application-defined SQL functions].
+** ^A parameter that is initialized using [sqlite3_bind_pointer()] appears
+** to be an ordinary SQL NULL value to everything other than
+** [sqlite3_value_pointer()].
+**
 ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
 ** for the [prepared statement] or with a prepared statement for which
 ** [sqlite3_step()] has been called more recently than [sqlite3_reset()],
@@ -3819,6 +3828,7 @@
 SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
                          void(*)(void*), unsigned char encoding);
 SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+SQLITE_API int sqlite3_bind_pointer(sqlite3_stmt*, int, void*);
 SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
 SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
 
@@ -4588,6 +4598,11 @@
 ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
 ** extract UTF-16 strings as big-endian and little-endian respectively.
 **
+** ^If [sqlite3_value] object V was initialized
+** using [sqlite3_bind_pointer(S,I,P)] or [sqlite3_result_pointer(C,P)], then
+** sqlite3_value_pointer(V) will return the pointer P.  Otherwise,
+** sqlite3_value_pointer(V) returns a NULL.
+**
 ** ^(The sqlite3_value_numeric_type() interface attempts to apply
 ** numeric affinity to the value.  This means that an attempt is
 ** made to convert the value to an integer or floating point.  If
@@ -4615,6 +4630,7 @@
 SQLITE_API const void *sqlite3_value_text16(sqlite3_value*);
 SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*);
 SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);
+SQLITE_API void *sqlite3_value_pointer(sqlite3_value*);
 SQLITE_API int sqlite3_value_type(sqlite3_value*);
 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
 
@@ -4627,10 +4643,6 @@
 ** information can be used to pass a limited amount of context from
 ** one SQL function to another.  Use the [sqlite3_result_subtype()]
 ** routine to set the subtype for the return value of an SQL function.
-**
-** SQLite makes no use of subtype itself.  It merely passes the subtype
-** from the result of one [application-defined SQL function] into the
-** input of another.
 */
 SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
 
@@ -4908,6 +4920,14 @@
 ** [unprotected sqlite3_value] object is required, so either
 ** kind of [sqlite3_value] object can be used with this interface.
 **
+** ^The sqlite3_result_pointer(C,P) interface sets the result to an
+** SQL NULL value, just like [sqlite3_result_null(C)], except that it
+** also associates the host-language pointer P with that NULL value such
+** that the pointer can be retrieved within an
+** [application-defined SQL function] using [sqlite3_value_pointer()].
+** This mechanism can be used to pass non-SQL values between
+** application-defined functions.
+**
 ** If these routines are called from within the different thread
 ** than the one containing the application-defined function that received
 ** the [sqlite3_context] pointer, the results are undefined.
@@ -4931,6 +4951,7 @@
 SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
 SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
 SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+SQLITE_API void sqlite3_result_pointer(sqlite3_context*, void*);
 SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);
 SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
 
diff --git a/third_party/sqlite/patches/0013-Add-new-interfaces-sqlite3_bind_pointer-sqlite3_resu.patch b/third_party/sqlite/patches/0013-Add-new-interfaces-sqlite3_bind_pointer-sqlite3_resu.patch
new file mode 100644
index 0000000..e65e75f
--- /dev/null
+++ b/third_party/sqlite/patches/0013-Add-new-interfaces-sqlite3_bind_pointer-sqlite3_resu.patch
@@ -0,0 +1,420 @@
+From 3dac6f0ce5313f60acc6b7abca1d431001ad0187 Mon Sep 17 00:00:00 2001
+From: Victor Costan <pwnall@chromium.org>
+Date: Sat, 15 Jul 2017 12:30:42 -0700
+Subject: [PATCH 13/13] Add new interfaces sqlite3_bind_pointer(),
+ sqlite3_result_pointer(), and sqlite3_value_pointer() used to safely move
+ pointer values through SQL without exposing underlying memory address
+ information.
+
+---
+ third_party/sqlite/src/ext/fts3/fts3.c     | 13 +++------
+ third_party/sqlite/src/ext/misc/carray.c   | 46 ++++++++++++++++++++++++++----
+ third_party/sqlite/src/ext/misc/remember.c |  6 ++--
+ third_party/sqlite/src/src/sqlite.h.in     | 29 ++++++++++++++++---
+ third_party/sqlite/src/src/vdbeInt.h       |  2 ++
+ third_party/sqlite/src/src/vdbeapi.c       | 24 ++++++++++++++++
+ third_party/sqlite/src/src/vdbemem.c       | 11 +++++++
+ third_party/sqlite/src/test/tabfunc01.test | 25 ++++++++--------
+ 8 files changed, 122 insertions(+), 34 deletions(-)
+
+diff --git a/third_party/sqlite/src/ext/fts3/fts3.c b/third_party/sqlite/src/ext/fts3/fts3.c
+index 827769881616..5d2f21d2937f 100644
+--- a/third_party/sqlite/src/ext/fts3/fts3.c
++++ b/third_party/sqlite/src/ext/fts3/fts3.c
+@@ -3347,9 +3347,8 @@ static int fts3ColumnMethod(
+     sqlite3_result_int64(pCtx, pCsr->iPrevId);
+   }else if( iCol==p->nColumn ){
+     /* The extra column whose name is the same as the table.
+-    ** Return a blob which is a pointer to the cursor.  */
+-    sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
+-    sqlite3_result_subtype(pCtx, '3');
++    ** Return a pointer to the cursor.  */
++    sqlite3_result_pointer(pCtx, pCsr);
+   }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
+     sqlite3_result_int64(pCtx, pCsr->iLangid);
+   }else{
+@@ -3561,17 +3560,13 @@ static int fts3FunctionArg(
+   sqlite3_value *pVal,            /* argv[0] passed to function */
+   Fts3Cursor **ppCsr              /* OUT: Store cursor handle here */
+ ){
+-  Fts3Cursor *pRet;
+-  if( sqlite3_value_type(pVal)!=SQLITE_BLOB
+-   || sqlite3_value_subtype(pVal)!='3'
+-   || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
+-  ){
++  Fts3Cursor *pRet = (Fts3Cursor*)sqlite3_value_pointer(pVal);
++  if( pRet==0 ){
+     char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
+     sqlite3_result_error(pContext, zErr, -1);
+     sqlite3_free(zErr);
+     return SQLITE_ERROR;
+   }
+-  memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
+   *ppCsr = pRet;
+   return SQLITE_OK;
+ }
+diff --git a/third_party/sqlite/src/ext/misc/carray.c b/third_party/sqlite/src/ext/misc/carray.c
+index 025eb5db2c0e..2fdff0cd3afc 100644
+--- a/third_party/sqlite/src/ext/misc/carray.c
++++ b/third_party/sqlite/src/ext/misc/carray.c
+@@ -73,7 +73,7 @@ typedef struct carray_cursor carray_cursor;
+ struct carray_cursor {
+   sqlite3_vtab_cursor base;  /* Base class - must be first */
+   sqlite3_int64 iRowid;      /* The rowid */
+-  sqlite3_int64 iPtr;        /* Pointer to array of values */
++  void* pPtr;                /* Pointer to the array of values */
+   sqlite3_int64 iCnt;        /* Number of integers in the array */
+   unsigned char eType;       /* One of the CARRAY_type values */
+ };
+@@ -167,7 +167,7 @@ static int carrayColumn(
+   carray_cursor *pCur = (carray_cursor*)cur;
+   sqlite3_int64 x = 0;
+   switch( i ){
+-    case CARRAY_COLUMN_POINTER:   x = pCur->iPtr;   break;
++    case CARRAY_COLUMN_POINTER:   return SQLITE_OK;
+     case CARRAY_COLUMN_COUNT:     x = pCur->iCnt;   break;
+     case CARRAY_COLUMN_CTYPE: {
+       sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
+@@ -232,8 +232,8 @@ static int carrayFilter(
+ ){
+   carray_cursor *pCur = (carray_cursor *)pVtabCursor;
+   if( idxNum ){
+-    pCur->iPtr = sqlite3_value_int64(argv[0]);
+-    pCur->iCnt = sqlite3_value_int64(argv[1]);
++    pCur->pPtr = sqlite3_value_pointer(argv[0]);
++    pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
+     if( idxNum<3 ){
+       pCur->eType = CARRAY_INT32;
+     }else{
+@@ -251,7 +251,7 @@ static int carrayFilter(
+       }
+     }
+   }else{
+-    pCur->iPtr = 0;
++    pCur->pPtr = 0;
+     pCur->iCnt = 0;
+   }
+   pCur->iRowid = 1;
+@@ -345,6 +345,34 @@ static sqlite3_module carrayModule = {
+   0,                         /* xRename */
+ };
+
++/*
++** For testing purpose in the TCL test harness, we need a method for
++** setting the pointer value.  The inttoptr(X) SQL function accomplishes
++** this.  Tcl script will bind an integer to X and the inttoptr() SQL
++** function will use sqlite3_result_pointer() to convert that integer into
++** a pointer.
++**
++** This is for testing on TCL only.
++*/
++#ifdef SQLITE_TEST
++static void inttoptrFunc(
++  sqlite3_context *context,
++  int argc,
++  sqlite3_value **argv
++){
++  void *p;
++  sqlite3_int64 i64;
++  i64 = sqlite3_value_int64(argv[0]);
++  if( sizeof(i64)==sizeof(p) ){
++    memcpy(&p, &i64, sizeof(p));
++  }else{
++    int i32 = i64 & 0xffffffff;
++    memcpy(&p, &i32, sizeof(p));
++  }
++  sqlite3_result_pointer(context, p);
++}
++#endif /* SQLITE_TEST */
++
+ #endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+ #ifdef _WIN32
+@@ -359,6 +387,12 @@ int sqlite3_carray_init(
+   SQLITE_EXTENSION_INIT2(pApi);
+ #ifndef SQLITE_OMIT_VIRTUALTABLE
+   rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
+-#endif
++#ifdef SQLITE_TEST
++  if( rc==SQLITE_OK ){
++    rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0,
++                                 inttoptrFunc, 0, 0);
++  }
++#endif /* SQLITE_TEST */
++#endif /* SQLITE_OMIT_VIRTUALTABLE */
+   return rc;
+ }
+diff --git a/third_party/sqlite/src/ext/misc/remember.c b/third_party/sqlite/src/ext/misc/remember.c
+index aa3eff8a3f8b..587d44a12cdc 100644
+--- a/third_party/sqlite/src/ext/misc/remember.c
++++ b/third_party/sqlite/src/ext/misc/remember.c
+@@ -44,11 +44,11 @@ static void rememberFunc(
+   sqlite3_value **argv
+ ){
+   sqlite3_int64 v;
+-  sqlite3_int64 ptr;
++  sqlite3_int64 *ptr;
+   assert( argc==2 );
+   v = sqlite3_value_int64(argv[0]);
+-  ptr = sqlite3_value_int64(argv[1]);
+-  *((sqlite3_int64*)ptr) = v;
++  ptr = sqlite3_value_pointer(argv[1]);
++  if( ptr ) *ptr = v;
+   sqlite3_result_int64(pCtx, v);
+ }
+
+diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in
+index fbbf4b9f2db3..371ae5848bbc 100644
+--- a/third_party/sqlite/src/src/sqlite.h.in
++++ b/third_party/sqlite/src/src/sqlite.h.in
+@@ -3786,6 +3786,15 @@ typedef struct sqlite3_context sqlite3_context;
+ ** [sqlite3_blob_open | incremental BLOB I/O] routines.
+ ** ^A negative value for the zeroblob results in a zero-length BLOB.
+ **
++** ^The sqlite3_bind_pointer(S,I,P) routine causes the I-th parameter in
++** [prepared statement] S to have an SQL value of NULL, but to also be
++** associated with the pointer P.
++** ^The sqlite3_bind_pointer() routine can be used to pass
++** host-language pointers into [application-defined SQL functions].
++** ^A parameter that is initialized using [sqlite3_bind_pointer()] appears
++** to be an ordinary SQL NULL value to everything other than
++** [sqlite3_value_pointer()].
++**
+ ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
+ ** for the [prepared statement] or with a prepared statement for which
+ ** [sqlite3_step()] has been called more recently than [sqlite3_reset()],
+@@ -3819,6 +3828,7 @@ int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+ int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
+                          void(*)(void*), unsigned char encoding);
+ int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
++int sqlite3_bind_pointer(sqlite3_stmt*, int, void*);
+ int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+ int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
+
+@@ -4588,6 +4598,11 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
+ ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
+ ** extract UTF-16 strings as big-endian and little-endian respectively.
+ **
++** ^If [sqlite3_value] object V was initialized
++** using [sqlite3_bind_pointer(S,I,P)] or [sqlite3_result_pointer(C,P)], then
++** sqlite3_value_pointer(V) will return the pointer P.  Otherwise,
++** sqlite3_value_pointer(V) returns a NULL.
++**
+ ** ^(The sqlite3_value_numeric_type() interface attempts to apply
+ ** numeric affinity to the value.  This means that an attempt is
+ ** made to convert the value to an integer or floating point.  If
+@@ -4615,6 +4630,7 @@ const unsigned char *sqlite3_value_text(sqlite3_value*);
+ const void *sqlite3_value_text16(sqlite3_value*);
+ const void *sqlite3_value_text16le(sqlite3_value*);
+ const void *sqlite3_value_text16be(sqlite3_value*);
++void *sqlite3_value_pointer(sqlite3_value*);
+ int sqlite3_value_type(sqlite3_value*);
+ int sqlite3_value_numeric_type(sqlite3_value*);
+
+@@ -4627,10 +4643,6 @@ int sqlite3_value_numeric_type(sqlite3_value*);
+ ** information can be used to pass a limited amount of context from
+ ** one SQL function to another.  Use the [sqlite3_result_subtype()]
+ ** routine to set the subtype for the return value of an SQL function.
+-**
+-** SQLite makes no use of subtype itself.  It merely passes the subtype
+-** from the result of one [application-defined SQL function] into the
+-** input of another.
+ */
+ unsigned int sqlite3_value_subtype(sqlite3_value*);
+
+@@ -4908,6 +4920,14 @@ typedef void (*sqlite3_destructor_type)(void*);
+ ** [unprotected sqlite3_value] object is required, so either
+ ** kind of [sqlite3_value] object can be used with this interface.
+ **
++** ^The sqlite3_result_pointer(C,P) interface sets the result to an
++** SQL NULL value, just like [sqlite3_result_null(C)], except that it
++** also associates the host-language pointer P with that NULL value such
++** that the pointer can be retrieved within an
++** [application-defined SQL function] using [sqlite3_value_pointer()].
++** This mechanism can be used to pass non-SQL values between
++** application-defined functions.
++**
+ ** If these routines are called from within the different thread
+ ** than the one containing the application-defined function that received
+ ** the [sqlite3_context] pointer, the results are undefined.
+@@ -4931,6 +4951,7 @@ void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
+ void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
+ void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
+ void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
++void sqlite3_result_pointer(sqlite3_context*, void*);
+ void sqlite3_result_zeroblob(sqlite3_context*, int n);
+ int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
+
+diff --git a/third_party/sqlite/src/src/vdbeInt.h b/third_party/sqlite/src/src/vdbeInt.h
+index 989cdfd34617..69eab56bc578 100644
+--- a/third_party/sqlite/src/src/vdbeInt.h
++++ b/third_party/sqlite/src/src/vdbeInt.h
+@@ -189,6 +189,7 @@ struct Mem {
+     double r;           /* Real value used when MEM_Real is set in flags */
+     i64 i;              /* Integer value used when MEM_Int is set in flags */
+     int nZero;          /* Used when bit MEM_Zero is set in flags */
++    void *pPtr;         /* Pointer when flags=MEM_NULL and eSubtype='p' */
+     FuncDef *pDef;      /* Used only when flags==MEM_Agg */
+     RowSet *pRowSet;    /* Used only when flags==MEM_RowSet */
+     VdbeFrame *pFrame;  /* Used when flags==MEM_Frame */
+@@ -474,6 +475,7 @@ void sqlite3VdbeMemSetInt64(Mem*, i64);
+ #else
+   void sqlite3VdbeMemSetDouble(Mem*, double);
+ #endif
++void sqlite3VdbeMemSetPointer(Mem*, void*);
+ void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
+ void sqlite3VdbeMemSetNull(Mem*);
+ void sqlite3VdbeMemSetZeroBlob(Mem*,int);
+diff --git a/third_party/sqlite/src/src/vdbeapi.c b/third_party/sqlite/src/src/vdbeapi.c
+index 6eb97f1d1ddb..c54490c077cd 100644
+--- a/third_party/sqlite/src/src/vdbeapi.c
++++ b/third_party/sqlite/src/src/vdbeapi.c
+@@ -198,6 +198,14 @@ unsigned int sqlite3_value_subtype(sqlite3_value *pVal){
+   Mem *pMem = (Mem*)pVal;
+   return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
+ }
++void *sqlite3_value_pointer(sqlite3_value *pVal){
++  Mem *p = (Mem*)pVal;
++  if( (p->flags & MEM_TypeMask)==(MEM_Null|MEM_Subtype) && p->eSubtype=='p' ){
++    return p->u.pPtr;
++  }else{
++    return 0;
++  }
++}
+ const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
+   return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
+ }
+@@ -376,6 +384,12 @@ void sqlite3_result_null(sqlite3_context *pCtx){
+   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+   sqlite3VdbeMemSetNull(pCtx->pOut);
+ }
++void sqlite3_result_pointer(sqlite3_context *pCtx, void *pPtr){
++  Mem *pOut = pCtx->pOut;
++  assert( sqlite3_mutex_held(pOut->db->mutex) );
++  sqlite3VdbeMemSetNull(pOut);
++  sqlite3VdbeMemSetPointer(pOut, pPtr);
++}
+ void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
+   Mem *pOut = pCtx->pOut;
+   assert( sqlite3_mutex_held(pOut->db->mutex) );
+@@ -1361,6 +1375,16 @@ int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
+   }
+   return rc;
+ }
++int sqlite3_bind_pointer(sqlite3_stmt *pStmt, int i, void *pPtr){
++  int rc;
++  Vdbe *p = (Vdbe*)pStmt;
++  rc = vdbeUnbind(p, i);
++  if( rc==SQLITE_OK ){
++    sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr);
++    sqlite3_mutex_leave(p->db->mutex);
++  }
++  return rc;
++}
+ int sqlite3_bind_text(
+   sqlite3_stmt *pStmt,
+   int i,
+diff --git a/third_party/sqlite/src/src/vdbemem.c b/third_party/sqlite/src/src/vdbemem.c
+index 656e19bfa890..2f7aedb32171 100644
+--- a/third_party/sqlite/src/src/vdbemem.c
++++ b/third_party/sqlite/src/src/vdbemem.c
+@@ -697,6 +697,17 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
+   }
+ }
+
++/*
++** Set the value stored in *pMem should already be a NULL.
++** Also store a pointer to go with it.
++*/
++void sqlite3VdbeMemSetPointer(Mem *pMem, void *pPtr){
++  assert( pMem->flags==MEM_Null );
++  pMem->flags = MEM_Null|MEM_Subtype;
++  pMem->u.pPtr = pPtr;
++  pMem->eSubtype = 'p';
++}
++
+ #ifndef SQLITE_OMIT_FLOATING_POINT
+ /*
+ ** Delete any previous value and set the value stored in *pMem to val,
+diff --git a/third_party/sqlite/src/test/tabfunc01.test b/third_party/sqlite/src/test/tabfunc01.test
+index dcaafa420c25..bb89aec1e706 100644
+--- a/third_party/sqlite/src/test/tabfunc01.test
++++ b/third_party/sqlite/src/test/tabfunc01.test
+@@ -150,62 +150,63 @@ do_execsql_test tabfunc01-600 {
+ do_test tabfunc01-700 {
+   set PTR1 [intarray_addr 5 7 13 17 23]
+   db eval {
+-    SELECT b FROM t600, carray($PTR1,5) WHERE a=value;
++    SELECT b FROM t600, carray(inttoptr($PTR1),5) WHERE a=value;
+   }
+ } {(005) (007) (013) (017) (023)}
+ do_test tabfunc01-701 {
+   db eval {
+-    SELECT b FROM t600 WHERE a IN carray($PTR1,5,'int32');
++    SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int32');
+   }
+ } {(005) (007) (013) (017) (023)}
+ do_test tabfunc01-702 {
+   db eval {
+-    SELECT b FROM t600 WHERE a IN carray($PTR1,4,'int32');
++    SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),4,'int32');
+   }
+ } {(005) (007) (013) (017)}
+ do_catchsql_test tabfunc01-710 {
+-  SELECT b FROM t600 WHERE a IN carray($PTR1,5,'int33');
++  SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int33');
+ } {1 {unknown datatype: 'int33'}}
+
+ do_test tabfunc01-720 {
+   set PTR2 [int64array_addr 5 7 13 17 23]
+   db eval {
+-    SELECT b FROM t600, carray($PTR2,5,'int64') WHERE a=value;
++    SELECT b FROM t600, carray(inttoptr($PTR2),5,'int64') WHERE a=value;
+   }
+ } {(005) (007) (013) (017) (023)}
+ do_test tabfunc01-721 {
+   db eval {
+     SELECT remember(123,$PTR2);
+-    SELECT value FROM carray($PTR2,5,'int64');
++    SELECT value FROM carray(inttoptr($PTR2),5,'int64');
+   }
+ } {123 123 7 13 17 23}
+ do_test tabfunc01-722 {
+   set PTR3 [expr {$PTR2+16}]
+   db eval {
+-    SELECT remember(987,$PTR3);
+-    SELECT value FROM carray($PTR2,5,'int64');
++    SELECT remember(987,inttoptr($PTR3));
++    SELECT value FROM carray(inttoptr($PTR2),5,'int64');
+   }
+ } {987 123 7 987 17 23}
+
+ do_test tabfunc01-730 {
+   set PTR4 [doublearray_addr 5.0 7.0 13.0 17.0 23.0]
+   db eval {
+-    SELECT b FROM t600, carray($PTR4,5,'double') WHERE a=value;
++    SELECT b FROM t600, carray(inttoptr($PTR4),5,'double') WHERE a=value;
+   }
+ } {(005) (007) (013) (017) (023)}
+
+ do_test tabfunc01-740 {
+   set PTR5 [textarray_addr x5 x7 x13 x17 x23]
+   db eval {
+-    SELECT b FROM t600, carray($PTR5,5,'char*') WHERE a=trim(value,'x');
++    SELECT b FROM t600, carray(inttoptr($PTR5),5,'char*')
++     WHERE a=trim(value,'x');
+   }
+ } {(005) (007) (013) (017) (023)}
+
+ do_test tabfunc01-750 {
+   db eval {
+     SELECT aa.value, bb.value, '|'
+-      FROM carray($PTR4,5,'double') AS aa
+-      JOIN carray($PTR5,5,'char*') AS bb ON aa.rowid=bb.rowid;
++      FROM carray(inttoptr($PTR4),5,'double') AS aa
++      JOIN carray(inttoptr($PTR5),5,'char*') AS bb ON aa.rowid=bb.rowid;
+   }
+ } {5.0 x5 | 7.0 x7 | 13.0 x13 | 17.0 x17 | 23.0 x23 |}
+
+--
+2.11
diff --git a/third_party/sqlite/src/ext/fts3/fts3.c b/third_party/sqlite/src/ext/fts3/fts3.c
index 8277698..5d2f21d 100644
--- a/third_party/sqlite/src/ext/fts3/fts3.c
+++ b/third_party/sqlite/src/ext/fts3/fts3.c
@@ -3347,9 +3347,8 @@
     sqlite3_result_int64(pCtx, pCsr->iPrevId);
   }else if( iCol==p->nColumn ){
     /* The extra column whose name is the same as the table.
-    ** Return a blob which is a pointer to the cursor.  */
-    sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
-    sqlite3_result_subtype(pCtx, '3');
+    ** Return a pointer to the cursor.  */
+    sqlite3_result_pointer(pCtx, pCsr);
   }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
     sqlite3_result_int64(pCtx, pCsr->iLangid);
   }else{
@@ -3561,17 +3560,13 @@
   sqlite3_value *pVal,            /* argv[0] passed to function */
   Fts3Cursor **ppCsr              /* OUT: Store cursor handle here */
 ){
-  Fts3Cursor *pRet;
-  if( sqlite3_value_type(pVal)!=SQLITE_BLOB 
-   || sqlite3_value_subtype(pVal)!='3'
-   || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
-  ){
+  Fts3Cursor *pRet = (Fts3Cursor*)sqlite3_value_pointer(pVal);
+  if( pRet==0 ){
     char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
     sqlite3_result_error(pContext, zErr, -1);
     sqlite3_free(zErr);
     return SQLITE_ERROR;
   }
-  memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
   *ppCsr = pRet;
   return SQLITE_OK;
 }
diff --git a/third_party/sqlite/src/ext/misc/carray.c b/third_party/sqlite/src/ext/misc/carray.c
index 025eb5db..2fdff0cd 100644
--- a/third_party/sqlite/src/ext/misc/carray.c
+++ b/third_party/sqlite/src/ext/misc/carray.c
@@ -73,7 +73,7 @@
 struct carray_cursor {
   sqlite3_vtab_cursor base;  /* Base class - must be first */
   sqlite3_int64 iRowid;      /* The rowid */
-  sqlite3_int64 iPtr;        /* Pointer to array of values */
+  void* pPtr;                /* Pointer to the array of values */
   sqlite3_int64 iCnt;        /* Number of integers in the array */
   unsigned char eType;       /* One of the CARRAY_type values */
 };
@@ -167,7 +167,7 @@
   carray_cursor *pCur = (carray_cursor*)cur;
   sqlite3_int64 x = 0;
   switch( i ){
-    case CARRAY_COLUMN_POINTER:   x = pCur->iPtr;   break;
+    case CARRAY_COLUMN_POINTER:   return SQLITE_OK;
     case CARRAY_COLUMN_COUNT:     x = pCur->iCnt;   break;
     case CARRAY_COLUMN_CTYPE: {
       sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
@@ -232,8 +232,8 @@
 ){
   carray_cursor *pCur = (carray_cursor *)pVtabCursor;
   if( idxNum ){
-    pCur->iPtr = sqlite3_value_int64(argv[0]);
-    pCur->iCnt = sqlite3_value_int64(argv[1]);
+    pCur->pPtr = sqlite3_value_pointer(argv[0]);
+    pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
     if( idxNum<3 ){
       pCur->eType = CARRAY_INT32;
     }else{
@@ -251,7 +251,7 @@
       }
     }
   }else{
-    pCur->iPtr = 0;
+    pCur->pPtr = 0;
     pCur->iCnt = 0;
   }
   pCur->iRowid = 1;
@@ -345,6 +345,34 @@
   0,                         /* xRename */
 };
 
+/*
+** For testing purpose in the TCL test harness, we need a method for
+** setting the pointer value.  The inttoptr(X) SQL function accomplishes
+** this.  Tcl script will bind an integer to X and the inttoptr() SQL
+** function will use sqlite3_result_pointer() to convert that integer into
+** a pointer.
+**
+** This is for testing on TCL only.
+*/
+#ifdef SQLITE_TEST
+static void inttoptrFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  void *p;
+  sqlite3_int64 i64;
+  i64 = sqlite3_value_int64(argv[0]);
+  if( sizeof(i64)==sizeof(p) ){
+    memcpy(&p, &i64, sizeof(p));
+  }else{
+    int i32 = i64 & 0xffffffff;
+    memcpy(&p, &i32, sizeof(p));
+  }
+  sqlite3_result_pointer(context, p);
+}
+#endif /* SQLITE_TEST */
+
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
 
 #ifdef _WIN32
@@ -359,6 +387,12 @@
   SQLITE_EXTENSION_INIT2(pApi);
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
-#endif
+#ifdef SQLITE_TEST
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0,
+                                 inttoptrFunc, 0, 0);
+  }
+#endif /* SQLITE_TEST */
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
   return rc;
 }
diff --git a/third_party/sqlite/src/ext/misc/remember.c b/third_party/sqlite/src/ext/misc/remember.c
index aa3eff8..587d44a 100644
--- a/third_party/sqlite/src/ext/misc/remember.c
+++ b/third_party/sqlite/src/ext/misc/remember.c
@@ -44,11 +44,11 @@
   sqlite3_value **argv
 ){
   sqlite3_int64 v;
-  sqlite3_int64 ptr;
+  sqlite3_int64 *ptr;
   assert( argc==2 );
   v = sqlite3_value_int64(argv[0]);
-  ptr = sqlite3_value_int64(argv[1]);
-  *((sqlite3_int64*)ptr) = v;
+  ptr = sqlite3_value_pointer(argv[1]);
+  if( ptr ) *ptr = v;
   sqlite3_result_int64(pCtx, v);
 }
 
diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in
index fbbf4b9f..371ae584 100644
--- a/third_party/sqlite/src/src/sqlite.h.in
+++ b/third_party/sqlite/src/src/sqlite.h.in
@@ -3786,6 +3786,15 @@
 ** [sqlite3_blob_open | incremental BLOB I/O] routines.
 ** ^A negative value for the zeroblob results in a zero-length BLOB.
 **
+** ^The sqlite3_bind_pointer(S,I,P) routine causes the I-th parameter in
+** [prepared statement] S to have an SQL value of NULL, but to also be
+** associated with the pointer P.
+** ^The sqlite3_bind_pointer() routine can be used to pass
+** host-language pointers into [application-defined SQL functions].
+** ^A parameter that is initialized using [sqlite3_bind_pointer()] appears
+** to be an ordinary SQL NULL value to everything other than
+** [sqlite3_value_pointer()].
+**
 ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
 ** for the [prepared statement] or with a prepared statement for which
 ** [sqlite3_step()] has been called more recently than [sqlite3_reset()],
@@ -3819,6 +3828,7 @@
 int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
                          void(*)(void*), unsigned char encoding);
 int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+int sqlite3_bind_pointer(sqlite3_stmt*, int, void*);
 int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
 int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
 
@@ -4588,6 +4598,11 @@
 ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
 ** extract UTF-16 strings as big-endian and little-endian respectively.
 **
+** ^If [sqlite3_value] object V was initialized
+** using [sqlite3_bind_pointer(S,I,P)] or [sqlite3_result_pointer(C,P)], then
+** sqlite3_value_pointer(V) will return the pointer P.  Otherwise,
+** sqlite3_value_pointer(V) returns a NULL.
+**
 ** ^(The sqlite3_value_numeric_type() interface attempts to apply
 ** numeric affinity to the value.  This means that an attempt is
 ** made to convert the value to an integer or floating point.  If
@@ -4615,6 +4630,7 @@
 const void *sqlite3_value_text16(sqlite3_value*);
 const void *sqlite3_value_text16le(sqlite3_value*);
 const void *sqlite3_value_text16be(sqlite3_value*);
+void *sqlite3_value_pointer(sqlite3_value*);
 int sqlite3_value_type(sqlite3_value*);
 int sqlite3_value_numeric_type(sqlite3_value*);
 
@@ -4627,10 +4643,6 @@
 ** information can be used to pass a limited amount of context from
 ** one SQL function to another.  Use the [sqlite3_result_subtype()]
 ** routine to set the subtype for the return value of an SQL function.
-**
-** SQLite makes no use of subtype itself.  It merely passes the subtype
-** from the result of one [application-defined SQL function] into the
-** input of another.
 */
 unsigned int sqlite3_value_subtype(sqlite3_value*);
 
@@ -4908,6 +4920,14 @@
 ** [unprotected sqlite3_value] object is required, so either
 ** kind of [sqlite3_value] object can be used with this interface.
 **
+** ^The sqlite3_result_pointer(C,P) interface sets the result to an
+** SQL NULL value, just like [sqlite3_result_null(C)], except that it
+** also associates the host-language pointer P with that NULL value such
+** that the pointer can be retrieved within an
+** [application-defined SQL function] using [sqlite3_value_pointer()].
+** This mechanism can be used to pass non-SQL values between
+** application-defined functions.
+**
 ** If these routines are called from within the different thread
 ** than the one containing the application-defined function that received
 ** the [sqlite3_context] pointer, the results are undefined.
@@ -4931,6 +4951,7 @@
 void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
 void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
 void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+void sqlite3_result_pointer(sqlite3_context*, void*);
 void sqlite3_result_zeroblob(sqlite3_context*, int n);
 int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
 
diff --git a/third_party/sqlite/src/src/vdbeInt.h b/third_party/sqlite/src/src/vdbeInt.h
index 989cdfd3..69eab56 100644
--- a/third_party/sqlite/src/src/vdbeInt.h
+++ b/third_party/sqlite/src/src/vdbeInt.h
@@ -189,6 +189,7 @@
     double r;           /* Real value used when MEM_Real is set in flags */
     i64 i;              /* Integer value used when MEM_Int is set in flags */
     int nZero;          /* Used when bit MEM_Zero is set in flags */
+    void *pPtr;         /* Pointer when flags=MEM_NULL and eSubtype='p' */
     FuncDef *pDef;      /* Used only when flags==MEM_Agg */
     RowSet *pRowSet;    /* Used only when flags==MEM_RowSet */
     VdbeFrame *pFrame;  /* Used when flags==MEM_Frame */
@@ -474,6 +475,7 @@
 #else
   void sqlite3VdbeMemSetDouble(Mem*, double);
 #endif
+void sqlite3VdbeMemSetPointer(Mem*, void*);
 void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
 void sqlite3VdbeMemSetNull(Mem*);
 void sqlite3VdbeMemSetZeroBlob(Mem*,int);
diff --git a/third_party/sqlite/src/src/vdbeapi.c b/third_party/sqlite/src/src/vdbeapi.c
index 6eb97f1..c54490c 100644
--- a/third_party/sqlite/src/src/vdbeapi.c
+++ b/third_party/sqlite/src/src/vdbeapi.c
@@ -198,6 +198,14 @@
   Mem *pMem = (Mem*)pVal;
   return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
 }
+void *sqlite3_value_pointer(sqlite3_value *pVal){
+  Mem *p = (Mem*)pVal;
+  if( (p->flags & MEM_TypeMask)==(MEM_Null|MEM_Subtype) && p->eSubtype=='p' ){
+    return p->u.pPtr;
+  }else{
+    return 0;
+  }
+}
 const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
   return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
 }
@@ -376,6 +384,12 @@
   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
   sqlite3VdbeMemSetNull(pCtx->pOut);
 }
+void sqlite3_result_pointer(sqlite3_context *pCtx, void *pPtr){
+  Mem *pOut = pCtx->pOut;
+  assert( sqlite3_mutex_held(pOut->db->mutex) );
+  sqlite3VdbeMemSetNull(pOut);
+  sqlite3VdbeMemSetPointer(pOut, pPtr);
+}
 void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
   Mem *pOut = pCtx->pOut;
   assert( sqlite3_mutex_held(pOut->db->mutex) );
@@ -1361,6 +1375,16 @@
   }
   return rc;
 }
+int sqlite3_bind_pointer(sqlite3_stmt *pStmt, int i, void *pPtr){
+  int rc;
+  Vdbe *p = (Vdbe*)pStmt;
+  rc = vdbeUnbind(p, i);
+  if( rc==SQLITE_OK ){
+    sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr);
+    sqlite3_mutex_leave(p->db->mutex);
+  }
+  return rc;
+}
 int sqlite3_bind_text( 
   sqlite3_stmt *pStmt, 
   int i, 
diff --git a/third_party/sqlite/src/src/vdbemem.c b/third_party/sqlite/src/src/vdbemem.c
index 656e19bfa..2f7aedb3 100644
--- a/third_party/sqlite/src/src/vdbemem.c
+++ b/third_party/sqlite/src/src/vdbemem.c
@@ -697,6 +697,17 @@
   }
 }
 
+/*
+** Set the value stored in *pMem should already be a NULL.
+** Also store a pointer to go with it.
+*/
+void sqlite3VdbeMemSetPointer(Mem *pMem, void *pPtr){
+  assert( pMem->flags==MEM_Null );
+  pMem->flags = MEM_Null|MEM_Subtype;
+  pMem->u.pPtr = pPtr;
+  pMem->eSubtype = 'p';
+}
+
 #ifndef SQLITE_OMIT_FLOATING_POINT
 /*
 ** Delete any previous value and set the value stored in *pMem to val,
diff --git a/third_party/sqlite/src/test/tabfunc01.test b/third_party/sqlite/src/test/tabfunc01.test
index dcaafa42..bb89aec 100644
--- a/third_party/sqlite/src/test/tabfunc01.test
+++ b/third_party/sqlite/src/test/tabfunc01.test
@@ -150,62 +150,63 @@
 do_test tabfunc01-700 {
   set PTR1 [intarray_addr 5 7 13 17 23]
   db eval {
-    SELECT b FROM t600, carray($PTR1,5) WHERE a=value;
+    SELECT b FROM t600, carray(inttoptr($PTR1),5) WHERE a=value;
   }
 } {(005) (007) (013) (017) (023)}
 do_test tabfunc01-701 {
   db eval {
-    SELECT b FROM t600 WHERE a IN carray($PTR1,5,'int32');
+    SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int32');
   }
 } {(005) (007) (013) (017) (023)}
 do_test tabfunc01-702 {
   db eval {
-    SELECT b FROM t600 WHERE a IN carray($PTR1,4,'int32');
+    SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),4,'int32');
   }
 } {(005) (007) (013) (017)}
 do_catchsql_test tabfunc01-710 {
-  SELECT b FROM t600 WHERE a IN carray($PTR1,5,'int33');
+  SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int33');
 } {1 {unknown datatype: 'int33'}}
 
 do_test tabfunc01-720 {
   set PTR2 [int64array_addr 5 7 13 17 23]
   db eval {
-    SELECT b FROM t600, carray($PTR2,5,'int64') WHERE a=value;
+    SELECT b FROM t600, carray(inttoptr($PTR2),5,'int64') WHERE a=value;
   }
 } {(005) (007) (013) (017) (023)}
 do_test tabfunc01-721 {
   db eval {
     SELECT remember(123,$PTR2);
-    SELECT value FROM carray($PTR2,5,'int64');
+    SELECT value FROM carray(inttoptr($PTR2),5,'int64');
   }
 } {123 123 7 13 17 23}
 do_test tabfunc01-722 {
   set PTR3 [expr {$PTR2+16}]
   db eval {
-    SELECT remember(987,$PTR3);
-    SELECT value FROM carray($PTR2,5,'int64');
+    SELECT remember(987,inttoptr($PTR3));
+    SELECT value FROM carray(inttoptr($PTR2),5,'int64');
   }
 } {987 123 7 987 17 23}
 
 do_test tabfunc01-730 {
   set PTR4 [doublearray_addr 5.0 7.0 13.0 17.0 23.0]
   db eval {
-    SELECT b FROM t600, carray($PTR4,5,'double') WHERE a=value;
+    SELECT b FROM t600, carray(inttoptr($PTR4),5,'double') WHERE a=value;
   }
 } {(005) (007) (013) (017) (023)}
 
 do_test tabfunc01-740 {
   set PTR5 [textarray_addr x5 x7 x13 x17 x23]
   db eval {
-    SELECT b FROM t600, carray($PTR5,5,'char*') WHERE a=trim(value,'x');
+    SELECT b FROM t600, carray(inttoptr($PTR5),5,'char*')
+     WHERE a=trim(value,'x');
   }
 } {(005) (007) (013) (017) (023)}
 
 do_test tabfunc01-750 {
   db eval {
     SELECT aa.value, bb.value, '|'
-      FROM carray($PTR4,5,'double') AS aa
-      JOIN carray($PTR5,5,'char*') AS bb ON aa.rowid=bb.rowid;
+      FROM carray(inttoptr($PTR4),5,'double') AS aa
+      JOIN carray(inttoptr($PTR5),5,'char*') AS bb ON aa.rowid=bb.rowid;
   }
 } {5.0 x5 | 7.0 x7 | 13.0 x13 | 17.0 x17 | 23.0 x23 |}
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 0e6abe6..18ecefa 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -103,9 +103,6 @@
       'Browser Side Navigation Linux': 'release_bot',
       'CFI Linux CF': 'cfi_full_cfi_diag_recover_release_static',
       'CFI Linux ToT': 'cfi_full_clang_tot_release_static',
-      'CFI Linux': 'cfi_release_static',
-      'CFI Linux Full': 'cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on_goma',
-      'CFI ThinLTO Linux ToT': 'cfi_thin_lto_clang_tot_full_symbols_release_static_use_lld',
       'Chromium Linux Goma Canary': 'release_bot',
       'Chromium Linux Goma Canary': 'release_bot',
       'Chromium Linux Goma Canary (clobber)': 'release_bot',
@@ -187,8 +184,6 @@
       'MD Top Chrome ChromeOS non-material': 'chromeos_with_codecs_debug_bot',
       'MD Top Chrome Win material': 'debug_bot_minimal_symbols',
       'MD Top Chrome Linux material': 'debug_bot',
-      'LTO Linux': 'official_goma_thin_lto_use_lld',
-      'LTO Linux Perf': 'official_goma_thin_lto_use_lld',
       'Libfuzzer Upload Linux ASan': 'release_libfuzzer_asan',
       'Libfuzzer Upload Linux ASan Debug': 'debug_libfuzzer_asan',
       'Libfuzzer Upload Linux MSan': 'release_libfuzzer_msan',
@@ -999,14 +994,6 @@
       'cfi_full', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'goma',
     ],
 
-    'cfi_release_static': [
-      'cfi', 'release', 'static',
-    ],
-
-    'cfi_thin_lto_clang_tot_full_symbols_release_static_use_lld': [
-      'cfi', 'thin_lto', 'clang_tot', 'full_symbols', 'release', 'static', 'use_lld',
-    ],
-
     'chrome_with_codecs_blink_logging_release_trybot': [
       'chrome_with_codecs', 'blink_logging', 'release_trybot',
     ],
@@ -1364,10 +1351,6 @@
       'official', 'goma', 'chromeos',
     ],
 
-    'official_goma_thin_lto_use_lld': [
-      'official', 'goma', 'thin_lto', 'use_lld',
-    ],
-
     'official_goma_minimal_symbols_android': [
       'official', 'goma', 'minimal_symbols', 'android',
     ],
@@ -1615,13 +1598,8 @@
       'gn_args': 'is_cast_audio_only=true enable_webrtc=false'
     },
 
-    'cfi': {
-      'gn_args': 'is_cfi=true',
-    },
-
     'cfi_full': {
-      'gn_args': 'use_cfi_cast=true',
-      'mixins': ['cfi'],
+      'gn_args': 'is_cfi=true use_cfi_cast=true',
     },
 
     'cfi_diag': {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0457c56e..561132c 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -31686,7 +31686,7 @@
   </summary>
 </histogram>
 
-<histogram name="MediaRouter.Source.FileFormat" enum="MediaContainers">
+<histogram name="MediaRouter.Source.LocalFileFormat" enum="MediaContainers">
   <owner>paezagon@chromium.org</owner>
   <summary>
     The file format of a local media Media Router session. This is recorded when
@@ -31695,6 +31695,16 @@
   </summary>
 </histogram>
 
+<histogram name="MediaRouter.Source.LocalFileSize" units="MB">
+  <owner>paezagon@chromium.org</owner>
+  <summary>
+    The file size of a local media Media Router session. This is recorded when a
+    casting session begins to keep track of what kind of media is being
+    streamed, specifically, whether it is a clip or a song, or a full length
+    film or podcast.
+  </summary>
+</histogram>
+
 <histogram name="MediaRouter.Ui.Action.CloseLatency" units="ms">
   <owner>apacible@chromium.org</owner>
   <summary>
@@ -92754,6 +92764,7 @@
       label="in-document existing page renderer-initiated navigation"/>
   <suffix name="ExistingPageDifferentDocumentRendererInitiated"
       label="existing page renderer-initiated navigation"/>
+  <suffix name="SamePage" label="same page navigation"/>
   <suffix name="NewSubFrame" label="new subframe navigation"/>
   <affected-histogram name="Navigation.SecureSchemeHasSSLStatus"/>
 </histogram_suffixes>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 9752d21a..80e436e1 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -582,7 +582,14 @@
   return result
 
 
-def generate_telemetry_test(swarming_dimensions, benchmark_name, browser):
+BENCHMARKS_TO_UPLOAD_TO_FLAKINESS_DASHBOARD = ['system_health.common_desktop',
+                                               'system_health.common_mobile',
+                                               'system_health.memory_desktop',
+                                               'system_health.memory_mobile']
+
+
+def generate_telemetry_test(swarming_dimensions,
+      benchmark_name, browser, for_fyi_waterfall=False):
   # The step name must end in 'test' or 'tests' in order for the
   # results to automatically show up on the flakiness dashboard.
   # (At least, this was true some time ago.) Continue to use this
@@ -598,6 +605,10 @@
   # When this is enabled on more than just windows machines we will need
   # --device=android
 
+  if (for_fyi_waterfall and
+      benchmark_name in BENCHMARKS_TO_UPLOAD_TO_FLAKINESS_DASHBOARD):
+    test_args.append('--output-format=json-test-results')
+
   ignore_task_failure = False
   step_name = benchmark_name
   if browser == 'reference':
@@ -672,7 +683,8 @@
 
 def generate_telemetry_tests(name, tester_config, benchmarks,
                              benchmark_sharding_map,
-                             benchmark_ref_build_blacklist):
+                             benchmark_ref_build_blacklist,
+                             for_fyi_waterfall=False):
   isolated_scripts = []
   # First determine the browser that you need based on the tester
   browser_name = ''
@@ -709,7 +721,7 @@
           dimension, device))
 
     test = generate_telemetry_test(
-      swarming_dimensions, benchmark.Name(), browser_name)
+      swarming_dimensions, benchmark.Name(), browser_name, for_fyi_waterfall)
     isolated_scripts.append(test)
     # Now create another executable for this benchmark on the reference browser
     # if it is not blacklisted from running on the reference browser.
@@ -717,7 +729,7 @@
     if not tester_config.get('replace_system_webview', False) and (
         benchmark.Name() not in benchmark_ref_build_blacklist):
       reference_test = generate_telemetry_test(
-        swarming_dimensions, benchmark.Name(),'reference')
+        swarming_dimensions, benchmark.Name(),'reference', for_fyi_waterfall)
       isolated_scripts.append(reference_test)
 
   return isolated_scripts
@@ -792,7 +804,7 @@
     # Generate benchmarks
     isolated_scripts = generate_telemetry_tests(
         name, config, all_benchmarks, benchmark_sharding_map,
-        BENCHMARK_REF_BUILD_BLACKLIST)
+        BENCHMARK_REF_BUILD_BLACKLIST, waterfall['name']=='chromium.perf.fyi')
     # Generate swarmed non-telemetry tests if present
     if config['swarming_dimensions'][0].get('perf_tests', False):
       isolated_scripts += generate_cplusplus_isolate_script_test(
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 786e3fe3..d30afe16e 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -148,6 +148,28 @@
         '--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk'])
     self.assertEquals(test['isolate_name'], 'telemetry_perf_webview_tests')
 
+  def testGenerateTelemetryTestsWithUploadToFlakinessDashboard(self):
+    swarming_dimensions = [{'os': 'SkyNet', 'id': 'T-850', 'pool': 'T-RIP'}]
+    test = perf_data_generator.generate_telemetry_test(
+        swarming_dimensions, 'system_health.common_desktop', 'release', True)
+    expected_generated_test = {
+        'override_compile_targets': ['telemetry_perf_tests'],
+        'args': ['system_health.common_desktop', '-v', '--upload-results',
+                 '--output-format=chartjson', '--browser=release',
+                 '--output-format=json-test-results'],
+        'swarming': {
+          'ignore_task_failure': False,
+          'dimension_sets': [{'os': 'SkyNet', 'id': 'T-850', 'pool': 'T-RIP'}],
+          'hard_timeout': 10800,
+          'can_use_on_swarming_builders': True,
+          'expiration': 36000,
+          'io_timeout': 3600,
+        },
+        'name': 'system_health.common_desktop',
+        'isolate_name': 'telemetry_perf_tests',
+      }
+    self.assertEquals(test, expected_generated_test)
+
   def testGenerateTelemetryTestsBlacklistedReferenceBuildTest(self):
     class BlacklistedBenchmark(benchmark.Benchmark):
       @classmethod
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 0bf08b5..e5197e7 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -65,7 +65,7 @@
   DCHECK(view_);
   DCHECK(client_);
 
-  frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_);
+  frame_sink_manager_->surface_manager()->RegisterFrameSinkId(frame_sink_id_);
   CreateNewCompositorFrameSinkSupport();
 }
 
@@ -73,7 +73,7 @@
   DestroyDelegatedContent();
   DetachFromCompositor();
   support_.reset();
-  frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_);
+  frame_sink_manager_->surface_manager()->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 void DelegatedFrameHostAndroid::SubmitCompositorFrame(
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 4cefb83..d97a15dc 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -127,6 +127,9 @@
 const int kSearchBoxTopPadding = 24;
 const int kSearchBoxBottomPadding = 21;
 
+// The preferred height of the search box.
+const int kSearchBoxPreferredHeight = 48;
+
 // The background border corner radius of the search box in fullscreen mode.
 const int kSearchBoxBorderCornerRadiusFullscreen = 24;
 
@@ -161,7 +164,7 @@
     "Apps.AppListSearchResultDistanceFromOrigin";
 
 // The height of tiles in search result.
-const int kSearchTileHeight = 90;
+const int kSearchTileHeight = 92;
 
 // The size of the search icon in the search box.
 const int kSearchIconSize = 24;
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index 3f8cddb..36d0be2 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -96,6 +96,7 @@
 APP_LIST_EXPORT extern const int kSearchBoxPadding;
 APP_LIST_EXPORT extern const int kSearchBoxTopPadding;
 APP_LIST_EXPORT extern const int kSearchBoxBottomPadding;
+APP_LIST_EXPORT extern const int kSearchBoxPreferredHeight;
 APP_LIST_EXPORT extern const int kSearchBoxBorderCornerRadiusFullscreen;
 
 APP_LIST_EXPORT extern size_t kMaxFolderItems;
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index da134c66..9d9a5bb 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -846,12 +846,9 @@
   // Make sure to layout |app_list_main_view_| and |speech_view_| at the
   // center of the widget.
   gfx::Rect centered_bounds = contents_bounds;
-  ContentsView* contents_view = app_list_main_view_->contents_view();
-  centered_bounds.ClampToCenteredSize(
-      gfx::Size(is_fullscreen_app_list_enabled_
-                    ? contents_view->GetMaximumContentsSize().width()
-                    : contents_view->GetDefaultContentsBounds().width(),
-                contents_bounds.height()));
+  centered_bounds.ClampToCenteredSize(gfx::Size(
+      app_list_main_view_->contents_view()->GetDefaultContentsBounds().width(),
+      contents_bounds.height()));
 
   app_list_main_view_->SetBoundsRect(centered_bounds);
 
@@ -866,7 +863,7 @@
   }
 
   if (is_fullscreen_app_list_enabled_) {
-    contents_view->Layout();
+    app_list_main_view_->contents_view()->Layout();
     app_list_background_shield_->SetBoundsRect(contents_bounds);
   }
 }
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index d58e40c..23a4465 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -372,8 +372,8 @@
   gfx::Rect search_box_bounds;
   if (is_fullscreen_app_list_enabled_) {
     search_box_bounds.set_size(GetSearchBoxView()->GetPreferredSize());
-    search_box_bounds.Offset((bounds().width() - search_box_bounds.width()) / 2,
-                             0);
+    search_box_bounds.Offset(
+        (GetDefaultContentsSize().width() - search_box_bounds.width()) / 2, 0);
     search_box_bounds.set_y(kSearchBoxTopPadding);
   } else {
     search_box_bounds =
@@ -392,24 +392,12 @@
 }
 
 gfx::Rect ContentsView::GetDefaultContentsBounds() const {
-  gfx::Size contents_size(GetDefaultContentsSize());
-  gfx::Point origin(0, GetDefaultSearchBoxBounds().bottom());
-  if (is_fullscreen_app_list_enabled_) {
-    origin.Offset((bounds().width() - contents_size.width()) / 2,
-                  kSearchBoxBottomPadding);
-  }
-  return gfx::Rect(origin, contents_size);
-}
-
-gfx::Size ContentsView::GetMaximumContentsSize() const {
-  int max_width = 0;
-  int max_height = 0;
-  for (AppListPage* page : app_list_pages_) {
-    gfx::Size size(page->GetPreferredSize());
-    max_width = std::max(size.width(), max_width);
-    max_height = std::max(size.height(), max_height);
-  }
-  return gfx::Size(max_width, max_height);
+  gfx::Rect bounds(gfx::Point(0, GetDefaultSearchBoxBounds().bottom() +
+                                     (is_fullscreen_app_list_enabled_
+                                          ? kSearchBoxBottomPadding
+                                          : 0)),
+                   GetDefaultContentsSize());
+  return bounds;
 }
 
 bool ContentsView::Back() {
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h
index e723e55..277a09460 100644
--- a/ui/app_list/views/contents_view.h
+++ b/ui/app_list/views/contents_view.h
@@ -119,9 +119,6 @@
   // specify their own custom layout.
   gfx::Rect GetDefaultContentsBounds() const;
 
-  // Returns the maximum preferred size of the all pages.
-  gfx::Size GetMaximumContentsSize() const;
-
   // Performs the 'back' action for the active page. Returns whether the action
   // was handled.
   bool Back();
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 38127c3..bfd701e 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -52,7 +52,6 @@
 constexpr int kInnerPadding = 24;
 constexpr int kPreferredWidth = 360;
 constexpr int kPreferredWidthFullscreen = 544;
-constexpr int kSearchBoxPreferredHeight = 48;
 
 constexpr SkColor kHintTextColor = SkColorSetARGBMacro(0xFF, 0xA0, 0xA0, 0xA0);
 
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc
index 7370eded..1f820a44 100644
--- a/ui/app_list/views/search_result_list_view.cc
+++ b/ui/app_list/views/search_result_list_view.cc
@@ -11,7 +11,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/time/time.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_result.h"
@@ -25,11 +24,10 @@
 
 namespace {
 
-constexpr int kMaxResults = 6;
-constexpr int kMaxResultsFullscreen = 5;
-constexpr int kTimeoutIndicatorHeight = 2;
-constexpr int kTimeoutFramerate = 60;
-constexpr SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
+const int kMaxResults = 6;
+const int kTimeoutIndicatorHeight = 2;
+const int kTimeoutFramerate = 60;
+const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
 
 }  // namespace
 
@@ -45,10 +43,7 @@
   results_container_->SetLayoutManager(
       new views::BoxLayout(views::BoxLayout::kVertical));
 
-  int max_results = features::IsFullscreenAppListEnabled()
-                        ? kMaxResultsFullscreen
-                        : kMaxResults;
-  for (int i = 0; i < max_results; ++i)
+  for (int i = 0; i < kMaxResults; ++i)
     results_container_->AddChildView(new SearchResultView(this));
   AddChildView(results_container_);
 
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index 20aa7566..92162c4d 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -34,7 +34,6 @@
 constexpr int kGroupSpacing = 6;
 constexpr int kTopPadding = 8;
 constexpr int kFullscreenHeight = 440;
-constexpr int kFullscreenWidth = 640;
 
 // The z-height of the search box and cards in this view.
 constexpr int kSearchResultZHeight = 1;
@@ -43,9 +42,6 @@
 constexpr int kSeparatorPadding = 12;
 constexpr int kSeparatorThickness = 1;
 
-// The height of the search box in this page.
-constexpr int kSearchBoxHeight = 56;
-
 constexpr SkColor kSeparatorColor = SkColorSetARGBMacro(0x1F, 0x00, 0x00, 0x00);
 
 // A container view that ensures the card background and the shadow are painted
@@ -96,7 +92,7 @@
     canvas->DrawRoundRect(bounds, corner_radius_, flags);
 
     // Draws a separator between SearchBoxView and SearchResultPageView.
-    bounds.set_y(kSearchBoxHeight);
+    bounds.set_y(kSearchBoxPreferredHeight);
     bounds.set_height(kSeparatorThickness);
     canvas->FillRect(bounds, kSeparatorColor);
   }
@@ -168,7 +164,7 @@
   if (is_fullscreen_app_list_enabled_) {
     // Leaves a placeholder area for the search box and the separator below it.
     scroller->SetBorder(views::CreateEmptyBorder(
-        gfx::Insets(kSearchBoxHeight + kSeparatorThickness, 0, 0, 0)));
+        gfx::Insets(kSearchBoxPreferredHeight + kSeparatorThickness, 0, 0, 0)));
   }
   scroller->SetContents(contents_view_);
   // Setting clip height is necessary to make ScrollView take into account its
@@ -249,12 +245,6 @@
   return "SearchResultPageView";
 }
 
-gfx::Size SearchResultPageView::CalculatePreferredSize() const {
-  if (!is_fullscreen_app_list_enabled_)
-    return GetDefaultContentsBounds().size();
-  return gfx::Size(kFullscreenWidth, kFullscreenHeight);
-}
-
 void SearchResultPageView::ClearSelectedIndex() {
   if (HasSelection())
     result_container_views_[selected_index_]->ClearSelectedIndex();
@@ -369,9 +359,9 @@
     return AppListPage::GetSearchBoxBounds();
   }
 
-  gfx::Rect onscreen_bounds(AppListPage::GetSearchBoxBounds());
-  onscreen_bounds.Offset((onscreen_bounds.width() - kFullscreenWidth) / 2, 0);
-  onscreen_bounds.set_size(GetPreferredSize());
+  gfx::Rect onscreen_bounds(GetDefaultContentsBounds());
+  onscreen_bounds.set_y(AppListPage::GetSearchBoxBounds().y());
+  onscreen_bounds.set_height(kFullscreenHeight);
   return onscreen_bounds;
 }
 
@@ -419,8 +409,9 @@
 gfx::Rect SearchResultPageView::GetSearchBoxBounds() const {
   gfx::Rect rect(AppListPage::GetSearchBoxBounds());
   if (is_fullscreen_app_list_enabled_) {
-    rect.Offset((rect.width() - kFullscreenWidth) / 2, 0);
-    rect.set_size(gfx::Size(kFullscreenWidth, kSearchBoxHeight));
+    gfx::Rect contents_bounds(GetDefaultContentsBounds());
+    rect.set_x(contents_bounds.x());
+    rect.set_width(contents_bounds.width());
   }
   return rect;
 }
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index c60e900..238eb707 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -39,7 +39,6 @@
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   const char* GetClassName() const override;
-  gfx::Size CalculatePreferredSize() const override;
 
   // AppListPage overrides:
   gfx::Rect GetPageBoundsForState(AppListModel::State state) const override;
diff --git a/ui/aura/mus/DEPS b/ui/aura/mus/DEPS
index c447b50..2f08e4b 100644
--- a/ui/aura/mus/DEPS
+++ b/ui/aura/mus/DEPS
@@ -11,6 +11,7 @@
   "+cc/surfaces/surface_reference_factory.h",
   "+components/discardable_memory/client/client_discardable_shared_memory_manager.h",
   "+components/viz/client",
+  "+components/viz/common/gpu",
   "+gpu/command_buffer/client/gpu_memory_buffer_manager.h",
   "+gpu/ipc/client/gpu_channel_host.h",
   "+mojo/public/cpp/system/buffer.h",
diff --git a/ui/aura/mus/mus_context_factory.cc b/ui/aura/mus/mus_context_factory.cc
index bfe06f3..2b6e28d 100644
--- a/ui/aura/mus/mus_context_factory.cc
+++ b/ui/aura/mus/mus_context_factory.cc
@@ -67,7 +67,7 @@
                  weak_ptr_factory_.GetWeakPtr(), compositor));
 }
 
-scoped_refptr<cc::ContextProvider>
+scoped_refptr<viz::ContextProvider>
 MusContextFactory::SharedMainThreadContextProvider() {
   if (!shared_main_thread_context_provider_) {
     scoped_refptr<gpu::GpuChannelHost> gpu_channel =
diff --git a/ui/aura/mus/mus_context_factory.h b/ui/aura/mus/mus_context_factory.h
index 4c84377b..e675b61 100644
--- a/ui/aura/mus/mus_context_factory.h
+++ b/ui/aura/mus/mus_context_factory.h
@@ -10,9 +10,9 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/renderer_settings.h"
 #include "cc/surfaces/surface_manager.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "services/ui/public/cpp/raster_thread_helper.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "ui/aura/aura_export.h"
@@ -46,7 +46,8 @@
   // ContextFactory:
   void CreateLayerTreeFrameSink(
       base::WeakPtr<ui::Compositor> compositor) override;
-  scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider() override;
+  scoped_refptr<viz::ContextProvider> SharedMainThreadContextProvider()
+      override;
   void RemoveCompositor(ui::Compositor* compositor) override;
   double GetRefreshRate() const override;
   gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
@@ -58,7 +59,7 @@
   ui::RasterThreadHelper raster_thread_helper_;
   ui::Gpu* gpu_;
   const cc::RendererSettings renderer_settings_;
-  scoped_refptr<cc::ContextProvider> shared_main_thread_context_provider_;
+  scoped_refptr<viz::ContextProvider> shared_main_thread_context_provider_;
 
   base::WeakPtrFactory<MusContextFactory> weak_ptr_factory_;
 
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 3a7adaf..4915a5e 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -105,7 +105,7 @@
 
 std::unique_ptr<viz::ClientLayerTreeFrameSink>
 WindowPortMus::RequestLayerTreeFrameSink(
-    scoped_refptr<cc::ContextProvider> context_provider,
+    scoped_refptr<viz::ContextProvider> context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
   cc::mojom::CompositorFrameSinkPtrInfo sink_info;
   cc::mojom::CompositorFrameSinkRequest sink_request =
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index 9311c812..68936714 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -90,7 +90,7 @@
              const ui::mojom::WindowTree::EmbedCallback& callback);
 
   std::unique_ptr<viz::ClientLayerTreeFrameSink> RequestLayerTreeFrameSink(
-      scoped_refptr<cc::ContextProvider> context_provider,
+      scoped_refptr<viz::ContextProvider> context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
 
  private:
diff --git a/ui/base/cocoa/touch_bar_util.h b/ui/base/cocoa/touch_bar_util.h
index d35d50f..94e0852b 100644
--- a/ui/base/cocoa/touch_bar_util.h
+++ b/ui/base/cocoa/touch_bar_util.h
@@ -7,6 +7,7 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include "base/mac/availability.h"
 #include "ui/base/ui_base_export.h"
 
 namespace ui {
@@ -19,6 +20,15 @@
 
 // Returns a stylized blue button for the touch bar. The button performs
 // |action| from the |target|.
+// The __attribute__ visibility annotation is necessary to work around a clang
+// bug: https://bugs.llvm.org/show_bug.cgi?id=33796.
+#if defined(UI_BASE_IMPLEMENTATION) && defined(COMPONENT_BUILD)
+// UI_BASE_EXPORT specifies "default" visibility.
+API_AVAILABLE(macosx(10.12.2))
+#else
+API_AVAILABLE(macosx(10.12.2))
+__attribute__((visibility("hidden")))
+#endif
 UI_BASE_EXPORT NSButton* GetBlueTouchBarButton(NSString* title,
                                                id target,
                                                SEL action);
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index b80e735..fb127d2 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -28,11 +28,11 @@
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
 #include "cc/output/begin_frame_args.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/latency_info_swap_promise.h"
 #include "cc/scheduler/begin_frame_source.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/quads/resource_format.h"
 #include "components/viz/common/resources/resource_settings.h"
 #include "components/viz/common/surfaces/local_surface_id_allocator.h"
@@ -69,8 +69,9 @@
       weak_ptr_factory_(this),
       lock_timeout_weak_ptr_factory_(this) {
   if (context_factory_private) {
-    context_factory_private->GetFrameSinkManager()->RegisterFrameSinkId(
-        frame_sink_id_);
+    context_factory_private->GetFrameSinkManager()
+        ->surface_manager()
+        ->RegisterFrameSinkId(frame_sink_id_);
   }
   root_web_layer_ = cc::Layer::Create();
 
@@ -217,7 +218,7 @@
       DCHECK(client.is_valid());
       manager->UnregisterFrameSinkHierarchy(frame_sink_id_, client);
     }
-    manager->InvalidateFrameSinkId(frame_sink_id_);
+    manager->surface_manager()->InvalidateFrameSinkId(frame_sink_id_);
   }
 }
 
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 3664ff58..12e8f3637 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -40,7 +40,6 @@
 namespace cc {
 class AnimationHost;
 class AnimationTimeline;
-class ContextProvider;
 class Layer;
 class LayerTreeDebugState;
 class LayerTreeFrameSink;
@@ -60,6 +59,7 @@
 
 namespace viz {
 class FrameSinkManager;
+class ContextProvider;
 class HostFrameSinkManager;
 class LocalSurfaceId;
 class ResourceSettings;
@@ -80,7 +80,7 @@
  public:
   virtual ~ContextFactoryObserver() {}
 
-  // Notifies that the ContextProvider returned from
+  // Notifies that the viz::ContextProvider returned from
   // ui::ContextFactory::SharedMainThreadContextProvider was lost.  When this
   // is called, the old resources (e.g. shared context, GL helper) still
   // exist, but are about to be destroyed. Getting a reference to those
@@ -153,7 +153,7 @@
 
   // Return a reference to a shared offscreen context provider usable from the
   // main thread.
-  virtual scoped_refptr<cc::ContextProvider>
+  virtual scoped_refptr<viz::ContextProvider>
   SharedMainThreadContextProvider() = 0;
 
   // Destroys per-compositor data.
diff --git a/ui/compositor/test/DEPS b/ui/compositor/test/DEPS
index b8b2938..c827f173 100644
--- a/ui/compositor/test/DEPS
+++ b/ui/compositor/test/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/viz/common/gpu",
   "+components/viz/service/display",
   "+components/viz/service/frame_sinks",
   "+gpu/command_buffer/client",
diff --git a/ui/compositor/test/fake_context_factory.cc b/ui/compositor/test/fake_context_factory.cc
index 73be3539..3417eeea 100644
--- a/ui/compositor/test/fake_context_factory.cc
+++ b/ui/compositor/test/fake_context_factory.cc
@@ -52,7 +52,7 @@
   compositor->SetLayerTreeFrameSink(std::move(frame_sink));
 }
 
-scoped_refptr<cc::ContextProvider>
+scoped_refptr<viz::ContextProvider>
 FakeContextFactory::SharedMainThreadContextProvider() {
   return nullptr;
 }
diff --git a/ui/compositor/test/fake_context_factory.h b/ui/compositor/test/fake_context_factory.h
index a69a1fc..8a29767 100644
--- a/ui/compositor/test/fake_context_factory.h
+++ b/ui/compositor/test/fake_context_factory.h
@@ -12,13 +12,16 @@
 
 namespace cc {
 class CompositorFrame;
-class ContextProvider;
 class FakeLayerTreeFrameSink;
 class ResourceSettings;
 class TestTaskGraphRunner;
 class TestGpuMemoryBufferManager;
 }
 
+namespace viz {
+class ContextProvider;
+}
+
 namespace ui {
 
 class FakeContextFactory : public ui::ContextFactory {
@@ -31,7 +34,8 @@
   // ui::ContextFactory:
   void CreateLayerTreeFrameSink(
       base::WeakPtr<ui::Compositor> compositor) override;
-  scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider() override;
+  scoped_refptr<viz::ContextProvider> SharedMainThreadContextProvider()
+      override;
   void RemoveCompositor(ui::Compositor* compositor) override;
   double GetRefreshRate() const override;
   gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index b36a7ac..c3630a7 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -12,13 +12,13 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread.h"
 #include "cc/base/switches.h"
-#include "cc/output/context_provider.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/output_surface_frame.h"
 #include "cc/output/texture_mailbox_deleter.h"
 #include "cc/scheduler/begin_frame_source.h"
 #include "cc/scheduler/delay_based_time_source.h"
 #include "cc/test/pixel_test_output_surface.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/surfaces/local_surface_id_allocator.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/display/display.h"
@@ -271,7 +271,7 @@
 void InProcessContextFactory::RemoveReflector(Reflector* reflector) {
 }
 
-scoped_refptr<cc::ContextProvider>
+scoped_refptr<viz::ContextProvider>
 InProcessContextFactory::SharedMainThreadContextProvider() {
   if (shared_main_thread_contexts_ &&
       shared_main_thread_contexts_->ContextGL()->GetGraphicsResetStatusKHR() ==
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 53be48be..aed33b5 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -63,7 +63,8 @@
                                              Layer* mirroring_layer) override;
   void RemoveReflector(Reflector* reflector) override;
 
-  scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider() override;
+  scoped_refptr<viz::ContextProvider> SharedMainThreadContextProvider()
+      override;
   void RemoveCompositor(Compositor* compositor) override;
   double GetRefreshRate() const override;
   gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
diff --git a/ui/compositor/test/in_process_context_provider.cc b/ui/compositor/test/in_process_context_provider.cc
index 45ce6fc..ef4918a 100644
--- a/ui/compositor/test/in_process_context_provider.cc
+++ b/ui/compositor/test/in_process_context_provider.cc
@@ -11,8 +11,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
-#include "cc/output/context_cache_controller.h"
 #include "cc/output/managed_memory_policy.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/gles2_lib.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
@@ -96,7 +96,7 @@
     if (!context_)
       return false;
 
-    cache_controller_.reset(new cc::ContextCacheController(
+    cache_controller_.reset(new viz::ContextCacheController(
         context_->GetImplementation(), base::ThreadTaskRunnerHandle::Get()));
   }
 
@@ -142,7 +142,7 @@
   return gr_context_->get();
 }
 
-cc::ContextCacheController* InProcessContextProvider::CacheController() {
+viz::ContextCacheController* InProcessContextProvider::CacheController() {
   DCHECK(context_thread_checker_.CalledOnValidThread());
   return cache_controller_.get();
 }
diff --git a/ui/compositor/test/in_process_context_provider.h b/ui/compositor/test/in_process_context_provider.h
index 2d2501f4..2fced24 100644
--- a/ui/compositor/test/in_process_context_provider.h
+++ b/ui/compositor/test/in_process_context_provider.h
@@ -13,7 +13,7 @@
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
-#include "cc/output/context_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ui/gfx/native_widget_types.h"
@@ -30,7 +30,7 @@
 
 namespace ui {
 
-class InProcessContextProvider : public cc::ContextProvider {
+class InProcessContextProvider : public viz::ContextProvider {
  public:
   static scoped_refptr<InProcessContextProvider> Create(
       const gpu::gles2::ContextCreationAttribHelper& attribs,
@@ -53,7 +53,7 @@
   gpu::gles2::GLES2Interface* ContextGL() override;
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
-  cc::ContextCacheController* CacheController() override;
+  viz::ContextCacheController* CacheController() override;
   void InvalidateGrContext(uint32_t state) override;
   base::Lock* GetLock() override;
   void SetLostContextCallback(
@@ -78,7 +78,7 @@
 
   std::unique_ptr<gpu::GLInProcessContext> context_;
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-  std::unique_ptr<cc::ContextCacheController> cache_controller_;
+  std::unique_ptr<viz::ContextCacheController> cache_controller_;
 
   gpu::gles2::ContextCreationAttribHelper attribs_;
   InProcessContextProvider* shared_context_;
diff --git a/ui/events/test/device_data_manager_test_api.h b/ui/events/test/device_data_manager_test_api.h
index a9d009c..92d26a3 100644
--- a/ui/events/test/device_data_manager_test_api.h
+++ b/ui/events/test/device_data_manager_test_api.h
@@ -6,6 +6,7 @@
 #define UI_EVENTS_TEST_DEVICE_DATA_MANAGER_TEST_API_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/macros.h"
 #include "ui/events/devices/events_devices_export.h"
@@ -14,6 +15,8 @@
 
 class DeviceDataManager;
 enum class StylusState;
+struct InputDevice;
+struct TouchscreenDevice;
 
 namespace test {
 
@@ -34,6 +37,13 @@
   void NotifyObserversStylusStateChanged(StylusState stylus_state);
   void OnDeviceListsComplete();
 
+  // Methods for updating DeviceDataManager's device lists. Notify* methods must
+  // be invoked separately to notify observers after making changes.
+  void SetTouchscreenDevices(const std::vector<TouchscreenDevice>& devices);
+  void SetKeyboardDevices(const std::vector<InputDevice>& devices);
+  void SetMouseDevices(const std::vector<InputDevice>& devices);
+  void SetTouchpadDevices(const std::vector<InputDevice>& devices);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DeviceDataManagerTestAPI);
 };
diff --git a/ui/events/test/device_data_manager_test_api_impl.cc b/ui/events/test/device_data_manager_test_api_impl.cc
index 2f37366a..58fbde5 100644
--- a/ui/events/test/device_data_manager_test_api_impl.cc
+++ b/ui/events/test/device_data_manager_test_api_impl.cc
@@ -53,5 +53,25 @@
   DeviceDataManager::GetInstance()->OnDeviceListsComplete();
 }
 
+void DeviceDataManagerTestAPI::SetTouchscreenDevices(
+    const std::vector<TouchscreenDevice>& devices) {
+  DeviceDataManager::GetInstance()->touchscreen_devices_ = devices;
+}
+
+void DeviceDataManagerTestAPI::SetKeyboardDevices(
+    const std::vector<InputDevice>& devices) {
+  DeviceDataManager::GetInstance()->keyboard_devices_ = devices;
+}
+
+void DeviceDataManagerTestAPI::SetMouseDevices(
+    const std::vector<InputDevice>& devices) {
+  DeviceDataManager::GetInstance()->mouse_devices_ = devices;
+}
+
+void DeviceDataManagerTestAPI::SetTouchpadDevices(
+    const std::vector<InputDevice>& devices) {
+  DeviceDataManager::GetInstance()->touchpad_devices_ = devices;
+}
+
 }  // namespace test
 }  // namespace ui
diff --git a/ui/events/test/device_data_manager_test_api_stub.cc b/ui/events/test/device_data_manager_test_api_stub.cc
index fa8ae97..a16b05f 100644
--- a/ui/events/test/device_data_manager_test_api_stub.cc
+++ b/ui/events/test/device_data_manager_test_api_stub.cc
@@ -48,5 +48,25 @@
   NOTREACHED();
 }
 
+void DeviceDataManagerTestAPI::SetTouchscreenDevices(
+    const std::vector<TouchscreenDevice>& devices) {
+  NOTREACHED();
+}
+
+void DeviceDataManagerTestAPI::SetKeyboardDevices(
+    const std::vector<InputDevice>& devices) {
+  NOTREACHED();
+}
+
+void DeviceDataManagerTestAPI::SetMouseDevices(
+    const std::vector<InputDevice>& devices) {
+  NOTREACHED();
+}
+
+void DeviceDataManagerTestAPI::SetTouchpadDevices(
+    const std::vector<InputDevice>& devices) {
+  NOTREACHED();
+}
+
 }  // namespace test
 }  // namespace ui
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index e369ae8..e579efd7 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -522,6 +522,7 @@
     "native_pixmap.h",
     "native_pixmap_handle.cc",
     "native_pixmap_handle.h",
+    "overlay_transform.h",
   ]
 
   if (!is_ios) {
diff --git a/ui/gfx/mojo/BUILD.gn b/ui/gfx/mojo/BUILD.gn
index 7a90efb..9722968c 100644
--- a/ui/gfx/mojo/BUILD.gn
+++ b/ui/gfx/mojo/BUILD.gn
@@ -10,6 +10,7 @@
     "buffer_types.mojom",
     "color_space.mojom",
     "icc_profile.mojom",
+    "overlay_transform.mojom",
     "selection_bound.mojom",
     "transform.mojom",
   ]
diff --git a/ui/gfx/mojo/overlay_transform.mojom b/ui/gfx/mojo/overlay_transform.mojom
new file mode 100644
index 0000000..af64e362
--- /dev/null
+++ b/ui/gfx/mojo/overlay_transform.mojom
@@ -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.
+
+module gfx.mojom;
+
+// gfx::OverlayTransform
+enum OverlayTransform {
+  OVERLAY_TRANSFORM_INVALID,
+  OVERLAY_TRANSFORM_NONE,
+  OVERLAY_TRANSFORM_FLIP_HORIZONTAL,
+  OVERLAY_TRANSFORM_FLIP_VERTICAL,
+  OVERLAY_TRANSFORM_ROTATE_90,
+  OVERLAY_TRANSFORM_ROTATE_180,
+  OVERLAY_TRANSFORM_ROTATE_270,
+  OVERLAY_TRANSFORM_LAST = OVERLAY_TRANSFORM_ROTATE_270
+};
diff --git a/ui/gfx/mojo/overlay_transform.typemap b/ui/gfx/mojo/overlay_transform.typemap
new file mode 100644
index 0000000..8dd9e1b
--- /dev/null
+++ b/ui/gfx/mojo/overlay_transform.typemap
@@ -0,0 +1,11 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//ui/gfx/mojo/overlay_transform.mojom"
+public_headers = [ "//ui/gfx/overlay_transform.h" ]
+traits_headers = [ "//ui/gfx/mojo/overlay_transform_struct_traits.h" ]
+public_deps = [
+  "//ui/gfx",
+]
+type_mappings = [ "gfx.mojom.OverlayTransform=gfx::OverlayTransform" ]
diff --git a/ui/gfx/mojo/overlay_transform_struct_traits.h b/ui/gfx/mojo/overlay_transform_struct_traits.h
new file mode 100644
index 0000000..e26f2db4
--- /dev/null
+++ b/ui/gfx/mojo/overlay_transform_struct_traits.h
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MOJO_OVERLAY_TRANSFORM_STRUCT_TRAITS_H_
+#define UI_GFX_MOJO_OVERLAY_TRANSFORM_STRUCT_TRAITS_H_
+
+#include "ui/gfx/mojo/overlay_transform.mojom.h"
+#include "ui/gfx/overlay_transform.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<gfx::mojom::OverlayTransform, gfx::OverlayTransform> {
+  static gfx::mojom::OverlayTransform ToMojom(gfx::OverlayTransform format) {
+    switch (format) {
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_INVALID:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_INVALID;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_90:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_90;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_180:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_180;
+      case gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270:
+        return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270;
+    }
+    NOTREACHED();
+    return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_INVALID;
+  }
+
+  static bool FromMojom(gfx::mojom::OverlayTransform input,
+                        gfx::OverlayTransform* out) {
+    switch (input) {
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_INVALID:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_INVALID;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_NONE:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_90:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_90;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_180:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_180;
+        return true;
+      case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270:
+        *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // UI_GFX_MOJO_OVERLAY_TRANSFORM_STRUCT_TRAITS_H_
diff --git a/ui/gfx/typemaps.gni b/ui/gfx/typemaps.gni
index d86ae08..375f74db 100644
--- a/ui/gfx/typemaps.gni
+++ b/ui/gfx/typemaps.gni
@@ -9,6 +9,7 @@
   "//ui/gfx/mojo/buffer_types.typemap",
   "//ui/gfx/mojo/color_space.typemap",
   "//ui/gfx/mojo/icc_profile.typemap",
+  "//ui/gfx/mojo/overlay_transform.typemap",
   "//ui/gfx/mojo/selection_bound.typemap",
   "//ui/gfx/mojo/transform.typemap",
   "//ui/gfx/range/mojo/range.typemap",
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 5047e94a..815c785 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -65,6 +65,8 @@
     "public/overlay_candidates_ozone.cc",
     "public/overlay_candidates_ozone.h",
     "public/overlay_manager_ozone.h",
+    "public/overlay_surface_candidate.cc",
+    "public/overlay_surface_candidate.h",
     "public/ozone_switches.cc",
     "public/ozone_switches.h",
     "public/surface_factory_ozone.cc",
@@ -97,8 +99,9 @@
     # things that would otherwise create a cycle.
     "//ui/base",
     "//ui/events/ozone/*",
-    "//ui/ozone/platform/*",
     "//ui/ozone/common/*",
+    "//ui/ozone/public/interfaces",
+    "//ui/ozone/platform/*",
   ]
 }
 
@@ -145,7 +148,6 @@
   deps = [
     ":generate_constructor_list",
     ":generate_ozone_platform_list",
-    "//ui/ozone/public/interfaces",
   ]
 
   deps += ozone_platform_deps
@@ -162,6 +164,7 @@
   visibility = [ "*" ]
   public_deps = [
     ":platform",
+    "//ui/ozone/public/interfaces",
   ]
 }
 
@@ -211,15 +214,14 @@
 }
 
 test("ozone_unittests") {
-  sources = [
-    "run_all_unittests.cc",
-  ]
-
   deps = [
     "//base/test:test_support",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/public/cpp/bindings",
     "//testing/gtest",
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
+    "//ui/ozone/public/interfaces:struct_trait_unit_test",
   ]
 
   # Add tests of platform internals.
diff --git a/ui/ozone/DEPS b/ui/ozone/DEPS
index 1ac3a7a..bcaff46e 100644
--- a/ui/ozone/DEPS
+++ b/ui/ozone/DEPS
@@ -10,3 +10,8 @@
   "+ui/gl",
   "+ui/platform_window",
 ]
+specific_include_rules = {
+  "run_all_unittests\.cc": [
+    "+mojo/edk/embedder/embedder.h",
+  ],
+}
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.cc b/ui/ozone/common/gpu/ozone_gpu_message_params.cc
index 7924ee0..29692cac 100644
--- a/ui/ozone/common/gpu/ozone_gpu_message_params.cc
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.cc
@@ -7,16 +7,15 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
 #include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
 
 namespace ui {
 
-DisplayMode_Params::DisplayMode_Params() {
-}
+DisplayMode_Params::DisplayMode_Params() {}
 
 DisplayMode_Params::~DisplayMode_Params() {}
 
-DisplaySnapshot_Params::DisplaySnapshot_Params() {
-}
+DisplaySnapshot_Params::DisplaySnapshot_Params() {}
 
 DisplaySnapshot_Params::DisplaySnapshot_Params(
     const DisplaySnapshot_Params& other) = default;
@@ -26,7 +25,7 @@
 OverlayCheck_Params::OverlayCheck_Params() {}
 
 OverlayCheck_Params::OverlayCheck_Params(
-    const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate)
+    const ui::OverlaySurfaceCandidate& candidate)
     : buffer_size(candidate.buffer_size),
       transform(candidate.transform),
       format(candidate.format),
@@ -37,8 +36,7 @@
 OverlayCheck_Params::OverlayCheck_Params(const OverlayCheck_Params& other) =
     default;
 
-OverlayCheck_Params::~OverlayCheck_Params() {
-}
+OverlayCheck_Params::~OverlayCheck_Params() {}
 
 bool OverlayCheck_Params::operator<(const OverlayCheck_Params& param) const {
   int lwidth = buffer_size.width();
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.h b/ui/ozone/common/gpu/ozone_gpu_message_params.h
index 0d2e8e3..0ad90a8 100644
--- a/ui/ozone/common/gpu/ozone_gpu_message_params.h
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.h
@@ -19,6 +19,7 @@
 #include "ui/ozone/public/overlay_candidates_ozone.h"
 
 namespace ui {
+class OverlaySurfaceCandidate;
 
 struct DisplayMode_Params {
   DisplayMode_Params();
@@ -56,8 +57,7 @@
 
 struct OverlayCheck_Params {
   OverlayCheck_Params();
-  OverlayCheck_Params(
-      const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate);
+  OverlayCheck_Params(const OverlaySurfaceCandidate& candidate);
   OverlayCheck_Params(const OverlayCheck_Params& other);
   ~OverlayCheck_Params();
 
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc
index 549c7ac..7930d017 100644
--- a/ui/ozone/demo/surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -131,7 +131,7 @@
   gfx::RectF display_rect(bounds_rect.x(), bounds_rect.y(), bounds_rect.width(),
                           bounds_rect.height());
 
-  OverlayCandidatesOzone::OverlaySurfaceCandidate overlay_candidate;
+  OverlaySurfaceCandidate overlay_candidate;
 
   // The bounds rectangle of the candidate overlay buffer.
   overlay_candidate.buffer_size = bounds_rect.size();
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.cc b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
index 9d5a968..b03f6f3e 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
@@ -15,13 +15,13 @@
 #include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h"
 #include "ui/ozone/platform/drm/host/drm_window_host.h"
 #include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
 #include "ui/ozone/public/ozone_switches.h"
 
 namespace ui {
 
-typedef OverlayCandidatesOzone::OverlaySurfaceCandidateList
-    OverlaySurfaceCandidateList;
-typedef OverlayCandidatesOzone::OverlaySurfaceCandidate OverlaySurfaceCandidate;
+using OverlaySurfaceCandidateList =
+    OverlayCandidatesOzone::OverlaySurfaceCandidateList;
 
 namespace {
 const size_t kMaxCacheSize = 200;
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.h b/ui/ozone/platform/drm/host/drm_overlay_manager.h
index 6e5a0ac..de51495 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.h
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.h
@@ -17,6 +17,7 @@
 
 namespace ui {
 class DrmWindowHostManager;
+class OverlaySurfaceCandidate;
 
 class DrmOverlayManager : public OverlayManagerOzone {
  public:
@@ -48,9 +49,8 @@
   void SendOverlayValidationRequest(
       const std::vector<OverlayCheck_Params>& new_params,
       gfx::AcceleratedWidget widget) const;
-  bool CanHandleCandidate(
-      const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate,
-      gfx::AcceleratedWidget widget) const;
+  bool CanHandleCandidate(const OverlaySurfaceCandidate& candidate,
+                          gfx::AcceleratedWidget widget) const;
 
   GpuThreadAdapter* proxy_;               // Not owned.
   DrmWindowHostManager* window_manager_;  // Not owned.
diff --git a/ui/ozone/public/interfaces/BUILD.gn b/ui/ozone/public/interfaces/BUILD.gn
index 7c9d6e9..48221f51 100644
--- a/ui/ozone/public/interfaces/BUILD.gn
+++ b/ui/ozone/public/interfaces/BUILD.gn
@@ -7,11 +7,7 @@
 mojom("interfaces") {
   sources = [
     "device_cursor.mojom",
-  ]
-
-  import_dirs = [
-    get_path_info("../../../..", "abspath"),
-    "//mojo/services",
+    "overlay_surface_candidate.mojom",
   ]
 
   public_deps = [
@@ -20,3 +16,32 @@
     "//ui/gfx/mojo",
   ]
 }
+
+source_set("struct_traits") {
+  sources = [
+    "overlay_surface_candidate_struct_traits.h",
+  ]
+  deps = [
+    "//ui/gfx/geometry/mojo:mojo",
+    "//ui/gfx/mojo:mojo",
+  ]
+  public_deps = [
+    ":interfaces",
+    ":interfaces_shared_cpp_sources",
+    "//mojo/public/cpp/bindings",
+  ]
+}
+
+source_set("struct_trait_unit_test") {
+  testonly = true
+
+  sources = [
+    "overlay_surface_candidate_struct_traits_unittest.cc",
+  ]
+
+  deps = [
+    ":interfaces",
+    "//testing/gtest",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/ui/ozone/public/interfaces/DEPS b/ui/ozone/public/interfaces/DEPS
new file mode 100644
index 0000000..ef8ad28
--- /dev/null
+++ b/ui/ozone/public/interfaces/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public",
+]
diff --git a/ui/ozone/public/interfaces/OWNERS b/ui/ozone/public/interfaces/OWNERS
index 08850f4..2c44a46 100644
--- a/ui/ozone/public/interfaces/OWNERS
+++ b/ui/ozone/public/interfaces/OWNERS
@@ -1,2 +1,6 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/ui/ozone/public/interfaces/overlay_surface_candidate.mojom b/ui/ozone/public/interfaces/overlay_surface_candidate.mojom
new file mode 100644
index 0000000..4903dce6
--- /dev/null
+++ b/ui/ozone/public/interfaces/overlay_surface_candidate.mojom
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ui.ozone.mojom;
+
+import "ui/gfx/geometry/mojo/geometry.mojom";
+import "ui/gfx/mojo/buffer_types.mojom";
+import "ui/gfx/mojo/overlay_transform.mojom";
+
+struct OverlaySurfaceCandidate {
+  // Transformation to apply to layer during composition.
+  gfx.mojom.OverlayTransform transform;
+  // Format of the buffer to composite.
+  gfx.mojom.BufferFormat format;
+  // Size of the buffer, in pixels.
+  gfx.mojom.Size buffer_size;
+  // Rect on the display to position the overlay to. Input rectangle may
+  // not have integer coordinates, but when accepting for overlay, must
+  // be modified by CheckOverlaySupport to output integer values.
+  gfx.mojom.RectF display_rect;
+  // Crop within the buffer to be placed inside |display_rect|.
+  gfx.mojom.RectF crop_rect;
+  // Quad geometry rect after applying the quad_transform().
+  gfx.mojom.Rect quad_rect_in_target_space;
+  // Clip rect in the target content space after composition.
+  gfx.mojom.Rect clip_rect;
+  // If the quad is clipped after composition.
+  bool is_clipped;
+  // Stacking order of the overlay plane relative to the main surface,
+  // which is 0. Signed to allow for "underlays".
+ int32 plane_z_order = 0;
+
+  // To be modified by the implementer if this candidate can go into
+  // an overlay.
+  bool overlay_handled;
+};
diff --git a/ui/ozone/public/interfaces/overlay_surface_candidate.typemap b/ui/ozone/public/interfaces/overlay_surface_candidate.typemap
new file mode 100644
index 0000000..6c9f347
--- /dev/null
+++ b/ui/ozone/public/interfaces/overlay_surface_candidate.typemap
@@ -0,0 +1,13 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//ui/ozone/public/interfaces/overlay_surface_candidate.mojom"
+public_headers = [ "//ui/ozone/public/overlay_surface_candidate.h" ]
+public_deps = [
+  "//ui/ozone:ozone_base",
+]
+traits_headers =
+    [ "//ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits.h" ]
+type_mappings =
+    [ "ui.ozone.mojom.OverlaySurfaceCandidate=ui::OverlaySurfaceCandidate" ]
diff --git a/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits.h b/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits.h
new file mode 100644
index 0000000..53c26839
--- /dev/null
+++ b/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits.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_OZONE_PUBLIC_INTERFACES_OVERLAY_SURFACE_CANDIDATE_STRUCT_TRAITS_H_
+#define UI_OZONE_PUBLIC_INTERFACES_OVERLAY_SURFACE_CANDIDATE_STRUCT_TRAITS_H_
+
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
+#include "ui/gfx/mojo/buffer_types_struct_traits.h"
+#include "ui/gfx/mojo/overlay_transform_struct_traits.h"
+#include "ui/ozone/public/interfaces/overlay_surface_candidate.mojom.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<ui::ozone::mojom::OverlaySurfaceCandidateDataView,
+                    ui::OverlaySurfaceCandidate> {
+  static const gfx::OverlayTransform& transform(
+      const ui::OverlaySurfaceCandidate& osc) {
+    return osc.transform;
+  }
+
+  static const gfx::BufferFormat& format(
+      const ui::OverlaySurfaceCandidate& osc) {
+    return osc.format;
+  }
+
+  static const gfx::Size& buffer_size(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.buffer_size;
+  }
+
+  static const gfx::RectF& display_rect(
+      const ui::OverlaySurfaceCandidate& osc) {
+    return osc.display_rect;
+  }
+
+  static const gfx::RectF& crop_rect(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.crop_rect;
+  }
+
+  static const gfx::Rect& quad_rect_in_target_space(
+      const ui::OverlaySurfaceCandidate& osc) {
+    return osc.quad_rect_in_target_space;
+  }
+
+  static const gfx::Rect& clip_rect(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.clip_rect;
+  }
+
+  static bool is_clipped(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.is_clipped;
+  }
+
+  static int plane_z_order(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.plane_z_order;
+  }
+
+  static bool overlay_handled(const ui::OverlaySurfaceCandidate& osc) {
+    return osc.overlay_handled;
+  }
+
+  static bool Read(ui::ozone::mojom::OverlaySurfaceCandidateDataView data,
+                   ui::OverlaySurfaceCandidate* out) {
+    out->is_clipped = data.is_clipped();
+    out->plane_z_order = data.plane_z_order();
+    out->overlay_handled = data.overlay_handled();
+    return data.ReadTransform(&out->transform) &&
+           data.ReadFormat(&out->format) &&
+           data.ReadBufferSize(&out->buffer_size) &&
+           data.ReadDisplayRect(&out->display_rect) &&
+           data.ReadCropRect(&out->crop_rect) &&
+           data.ReadQuadRectInTargetSpace(&out->quad_rect_in_target_space) &&
+           data.ReadClipRect(&out->clip_rect);
+  }
+};
+
+}  // namespace mojo
+
+#endif  // UI_OZONE_PUBLIC_INTERFACES_OVERLAY_SURFACE_CANDIDATE_STRUCT_TRAITS_H_
diff --git a/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits_unittest.cc b/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits_unittest.cc
new file mode 100644
index 0000000..8ec1fd0c
--- /dev/null
+++ b/ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits_unittest.cc
@@ -0,0 +1,73 @@
+// 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 <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/ozone/public/interfaces/overlay_surface_candidate.mojom.h"
+#include "ui/ozone/public/interfaces/overlay_surface_candidate_struct_traits.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace ui {
+
+namespace {
+
+class OverlaySurfaceCandidateStructTraitsTest : public testing::Test {
+ public:
+  OverlaySurfaceCandidateStructTraitsTest() {}
+};
+
+}  // namespace
+
+TEST_F(OverlaySurfaceCandidateStructTraitsTest, FieldsEqual) {
+  ui::OverlaySurfaceCandidate input;
+
+  input.transform = gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
+  input.format = gfx::BufferFormat::YUV_420_BIPLANAR;
+  input.buffer_size = gfx::Size(6, 7);
+  input.display_rect = gfx::RectF(1., 2., 3., 4.);
+  input.crop_rect = gfx::RectF(10., 20., 30., 40.);
+  input.quad_rect_in_target_space = gfx::Rect(101, 201, 301, 401);
+  input.clip_rect = gfx::Rect(11, 21, 31, 41);
+  input.is_clipped = true;
+  input.plane_z_order = 42;
+  input.overlay_handled = true;
+
+  ui::OverlaySurfaceCandidate output;
+
+  bool success = ui::ozone::mojom::OverlaySurfaceCandidate::Deserialize(
+      ui::ozone::mojom::OverlaySurfaceCandidate::Serialize(&input), &output);
+
+  EXPECT_TRUE(success);
+
+  EXPECT_EQ(input.transform, output.transform);
+  EXPECT_EQ(input.format, output.format);
+  EXPECT_EQ(input.buffer_size, output.buffer_size);
+  EXPECT_EQ(input.display_rect, output.display_rect);
+  EXPECT_EQ(input.crop_rect, output.crop_rect);
+  EXPECT_EQ(input.quad_rect_in_target_space, output.quad_rect_in_target_space);
+  EXPECT_EQ(input.clip_rect, output.clip_rect);
+  EXPECT_EQ(input.is_clipped, output.is_clipped);
+  EXPECT_EQ(input.plane_z_order, output.plane_z_order);
+  EXPECT_EQ(input.overlay_handled, output.overlay_handled);
+}
+
+TEST_F(OverlaySurfaceCandidateStructTraitsTest, FalseBools) {
+  ui::OverlaySurfaceCandidate input;
+
+  input.is_clipped = false;
+  input.overlay_handled = false;
+
+  ui::OverlaySurfaceCandidate output;
+
+  bool success = ui::ozone::mojom::OverlaySurfaceCandidate::Deserialize(
+      ui::ozone::mojom::OverlaySurfaceCandidate::Serialize(&input), &output);
+
+  EXPECT_TRUE(success);
+  EXPECT_EQ(input.is_clipped, output.is_clipped);
+  EXPECT_EQ(input.overlay_handled, output.overlay_handled);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/interfaces/typemaps.gni b/ui/ozone/public/interfaces/typemaps.gni
new file mode 100644
index 0000000..860a0b3
--- /dev/null
+++ b/ui/ozone/public/interfaces/typemaps.gni
@@ -0,0 +1,5 @@
+# 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.
+
+typemaps = [ "//ui/ozone/public/interfaces/overlay_surface_candidate.typemap" ]
diff --git a/ui/ozone/public/overlay_candidates_ozone.cc b/ui/ozone/public/overlay_candidates_ozone.cc
index d6fdc00..e6cc3c8 100644
--- a/ui/ozone/public/overlay_candidates_ozone.cc
+++ b/ui/ozone/public/overlay_candidates_ozone.cc
@@ -8,21 +8,11 @@
 
 namespace ui {
 
-OverlayCandidatesOzone::OverlaySurfaceCandidate::OverlaySurfaceCandidate()
-    : is_clipped(false) {}
-
-OverlayCandidatesOzone::OverlaySurfaceCandidate::OverlaySurfaceCandidate(
-    const OverlaySurfaceCandidate& other) = default;
-
-OverlayCandidatesOzone::OverlaySurfaceCandidate::~OverlaySurfaceCandidate() {
-}
-
 void OverlayCandidatesOzone::CheckOverlaySupport(
     OverlaySurfaceCandidateList* surfaces) {
   NOTREACHED();
 }
 
-OverlayCandidatesOzone::~OverlayCandidatesOzone() {
-}
+OverlayCandidatesOzone::~OverlayCandidatesOzone() {}
 
 }  // namespace ui
diff --git a/ui/ozone/public/overlay_candidates_ozone.h b/ui/ozone/public/overlay_candidates_ozone.h
index 6637e865..760721fee 100644
--- a/ui/ozone/public/overlay_candidates_ozone.h
+++ b/ui/ozone/public/overlay_candidates_ozone.h
@@ -7,12 +7,8 @@
 
 #include <vector>
 
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/overlay_transform.h"
 #include "ui/ozone/ozone_base_export.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
 
 namespace ui {
 
@@ -21,38 +17,6 @@
 // class from SurfaceFactoryOzone given an AcceleratedWidget.
 class OZONE_BASE_EXPORT OverlayCandidatesOzone {
  public:
-  struct OverlaySurfaceCandidate {
-    OverlaySurfaceCandidate();
-    OverlaySurfaceCandidate(const OverlaySurfaceCandidate& other);
-    ~OverlaySurfaceCandidate();
-
-    // Transformation to apply to layer during composition.
-    gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE;
-    // Format of the buffer to composite.
-    gfx::BufferFormat format = gfx::BufferFormat::BGRA_8888;
-    // Size of the buffer, in pixels.
-    gfx::Size buffer_size;
-    // Rect on the display to position the overlay to. Input rectangle may
-    // not have integer coordinates, but when accepting for overlay, must
-    // be modified by CheckOverlaySupport to output integer values.
-    gfx::RectF display_rect;
-    // Crop within the buffer to be placed inside |display_rect|.
-    gfx::RectF crop_rect;
-    // Quad geometry rect after applying the quad_transform().
-    gfx::Rect quad_rect_in_target_space;
-    // Clip rect in the target content space after composition.
-    gfx::Rect clip_rect;
-    // If the quad is clipped after composition.
-    bool is_clipped;
-    // Stacking order of the overlay plane relative to the main surface,
-    // which is 0. Signed to allow for "underlays".
-    int plane_z_order = 0;
-
-    // To be modified by the implementer if this candidate can go into
-    // an overlay.
-    bool overlay_handled = false;
-  };
-
   typedef std::vector<OverlaySurfaceCandidate> OverlaySurfaceCandidateList;
 
   // A list of possible overlay candidates is presented to this function.
diff --git a/ui/ozone/public/overlay_surface_candidate.cc b/ui/ozone/public/overlay_surface_candidate.cc
new file mode 100644
index 0000000..3612d21
--- /dev/null
+++ b/ui/ozone/public/overlay_surface_candidate.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace ui {
+
+OverlaySurfaceCandidate::OverlaySurfaceCandidate() : is_clipped(false) {}
+
+OverlaySurfaceCandidate::OverlaySurfaceCandidate(
+    const OverlaySurfaceCandidate& other) = default;
+
+OverlaySurfaceCandidate::~OverlaySurfaceCandidate() {}
+
+}  // namespace ui
diff --git a/ui/ozone/public/overlay_surface_candidate.h b/ui/ozone/public/overlay_surface_candidate.h
new file mode 100644
index 0000000..349ec1bd
--- /dev/null
+++ b/ui/ozone/public/overlay_surface_candidate.h
@@ -0,0 +1,52 @@
+// 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_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_
+#define UI_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_
+
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace ui {
+
+class OZONE_BASE_EXPORT OverlaySurfaceCandidate {
+ public:
+  OverlaySurfaceCandidate();
+  OverlaySurfaceCandidate(const OverlaySurfaceCandidate& other);
+  ~OverlaySurfaceCandidate();
+
+  // Transformation to apply to layer during composition.
+  gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE;
+  // Format of the buffer to composite.
+  gfx::BufferFormat format = gfx::BufferFormat::BGRA_8888;
+  // Size of the buffer, in pixels.
+  gfx::Size buffer_size;
+  // Rect on the display to position the overlay to. Input rectangle may
+  // not have integer coordinates, but when accepting for overlay, must
+  // be modified by CheckOverlaySupport to output integer values.
+  gfx::RectF display_rect;
+  // Crop within the buffer to be placed inside |display_rect|.
+  gfx::RectF crop_rect;
+  // Quad geometry rect after applying the quad_transform().
+  gfx::Rect quad_rect_in_target_space;
+  // Clip rect in the target content space after composition.
+  gfx::Rect clip_rect;
+  // If the quad is clipped after composition.
+  bool is_clipped;
+  // Stacking order of the overlay plane relative to the main surface,
+  // which is 0. Signed to allow for "underlays".
+  int plane_z_order = 0;
+
+  // To be modified by the implementer if this candidate can go into
+  // an overlay.
+  bool overlay_handled = false;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_
\ No newline at end of file
diff --git a/ui/ozone/run_all_unittests.cc b/ui/ozone/run_all_unittests.cc
deleted file mode 100644
index 9f2ad71..0000000
--- a/ui/ozone/run_all_unittests.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "build/build_config.h"
-
-namespace {
-
-class OzoneTestSuite : public base::TestSuite {
- public:
-  OzoneTestSuite(int argc, char** argv);
-
- protected:
-  // base::TestSuite:
-  void Initialize() override;
-  void Shutdown() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(OzoneTestSuite);
-};
-
-OzoneTestSuite::OzoneTestSuite(int argc, char** argv)
-    : base::TestSuite(argc, argv) {}
-
-void OzoneTestSuite::Initialize() {
-  base::TestSuite::Initialize();
-}
-
-void OzoneTestSuite::Shutdown() {
-  base::TestSuite::Shutdown();
-}
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  OzoneTestSuite test_suite(argc, argv);
-
-  return base::LaunchUnitTests(argc,
-                               argv,
-                               base::Bind(&OzoneTestSuite::Run,
-                                          base::Unretained(&test_suite)));
-}
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 0fc7e281e..e9125d6 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -627,7 +627,17 @@
       SetSelection(part.menu ? part.menu : state_.item, SELECTION_OPEN_SUBMENU);
     }
   } else if (part.type == MenuPart::NONE) {
-    ShowSiblingMenu(source, event.location());
+    // If there is a sibling menu, show it. Otherwise, if the user has selected
+    // a menu item with no accompanying sibling menu or submenu, move selection
+    // back to the parent menu item.
+    if (!ShowSiblingMenu(source, event.location())) {
+      if (!part.is_scroll() && pending_state_.item &&
+          pending_state_.item->GetParentMenuItem() &&
+          !pending_state_.item->SubmenuIsShowing()) {
+        SetSelection(pending_state_.item->GetParentMenuItem(),
+                     SELECTION_OPEN_SUBMENU);
+      }
+    }
   }
   UpdateActiveMouseView(source, event, mouse_menu);
 
@@ -1329,8 +1339,7 @@
     case ui::VKEY_ESCAPE:
       if (!state_.item->GetParentMenuItem() ||
           (!state_.item->GetParentMenuItem()->GetParentMenuItem() &&
-           (!state_.item->HasSubmenu() ||
-            !state_.item->GetSubmenu()->IsShowing()))) {
+           (!state_.item->SubmenuIsShowing()))) {
         // User pressed escape and current menu has no submenus. If we are
         // nested, close the current menu on the stack. Otherwise fully exit the
         // menu.
@@ -1633,7 +1642,7 @@
     const gfx::Point& screen_loc) {
   MenuPart part;
   for (; item; item = item->GetParentMenuItem()) {
-    if (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
+    if (item->SubmenuIsShowing() &&
         GetMenuPartByScreenCoordinateImpl(item->GetSubmenu(), screen_loc,
                                           &part)) {
       return part;
@@ -1764,8 +1773,7 @@
     } else {
       state_.submenu_open = false;
     }
-  } else if (state_.item->HasSubmenu() &&
-             state_.item->GetSubmenu()->IsShowing()) {
+  } else if (state_.item->SubmenuIsShowing()) {
     state_.item->GetSubmenu()->Hide();
   }
 
@@ -1775,7 +1783,7 @@
     bool found = false;
     for (MenuItemView* item = state_.item; item && !found;
          item = item->GetParentMenuItem()) {
-      found = (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
+      found = (item->SubmenuIsShowing() &&
                item->GetSubmenu() == scroll_task_->submenu());
     }
     if (!found)
@@ -2184,8 +2192,7 @@
     SelectionIncrementDirectionType direction) {
   MenuItemView* item = pending_state_.item;
   DCHECK(item);
-  if (pending_state_.submenu_open && item->HasSubmenu() &&
-      item->GetSubmenu()->IsShowing()) {
+  if (pending_state_.submenu_open && item->SubmenuIsShowing()) {
     // A menu is selected and open, but none of its children are selected,
     // select the first menu item that is visible and enabled.
     if (item->GetSubmenu()->GetMenuItemCount()) {
@@ -2278,7 +2285,7 @@
   DCHECK(item);
   if (!item->GetParentMenuItem())
     return;
-  if (item->HasSubmenu() && item->GetSubmenu()->IsShowing())
+  if (item->SubmenuIsShowing())
     SetSelection(item, SELECTION_UPDATE_IMMEDIATELY);
   else if (item->GetParentMenuItem()->GetParentMenuItem())
     SetSelection(item->GetParentMenuItem(), SELECTION_UPDATE_IMMEDIATELY);
@@ -2346,7 +2353,7 @@
   base::char16 char_array[] = { character, 0 };
   base::char16 key = base::i18n::ToLower(char_array)[0];
   MenuItemView* item = pending_state_.item;
-  if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing())
+  if (!item->SubmenuIsShowing())
     item = item->GetParentMenuItem();
   DCHECK(item);
   DCHECK(item->HasSubmenu());
@@ -2672,8 +2679,7 @@
     SetSelection(part.menu, SELECTION_OPEN_SUBMENU);
   } else if (!part.is_scroll() && pending_state_.item &&
              pending_state_.item->GetParentMenuItem() &&
-             (!pending_state_.item->HasSubmenu() ||
-              !pending_state_.item->GetSubmenu()->IsShowing())) {
+             !pending_state_.item->SubmenuIsShowing()) {
     // On exit if the user hasn't selected an item with a submenu, move the
     // selection back to the parent menu item.
     SetSelection(pending_state_.item->GetParentMenuItem(),
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 7608aeae..c5aa7ab 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -1417,6 +1417,47 @@
   EXPECT_EQ(menu_controller_delegate(), GetCurrentDelegate());
 }
 
+// Drag the mouse from an external view into a menu
+// When the mouse leaves the menu while still in the process of dragging
+// the menu item view highlight should turn off
+TEST_F(MenuControllerTest, DragFromViewIntoMenuAndExit) {
+  SubmenuView* sub_menu = menu_item()->GetSubmenu();
+  MenuItemView* first_item = sub_menu->GetMenuItemAt(0);
+
+  std::unique_ptr<View> drag_view = base::MakeUnique<View>();
+  drag_view->SetBoundsRect(gfx::Rect(0, 500, 100, 100));
+  sub_menu->ShowAt(owner(), gfx::Rect(0, 0, 100, 100), false);
+  gfx::Point press_location(drag_view->bounds().CenterPoint());
+  gfx::Point drag_location(first_item->bounds().CenterPoint());
+  gfx::Point release_location(200, 50);
+
+  // Begin drag on an external view
+  ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, press_location,
+                             press_location, ui::EventTimeForNow(),
+                             ui::EF_LEFT_MOUSE_BUTTON, 0);
+  drag_view->OnMousePressed(press_event);
+
+  // Drag into a menu item
+  ui::MouseEvent drag_event_enter(ui::ET_MOUSE_DRAGGED, drag_location,
+                                  drag_location, ui::EventTimeForNow(),
+                                  ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ProcessMouseDragged(sub_menu, drag_event_enter);
+  EXPECT_TRUE(first_item->IsSelected());
+
+  // Drag out of the menu item
+  ui::MouseEvent drag_event_exit(ui::ET_MOUSE_DRAGGED, release_location,
+                                 release_location, ui::EventTimeForNow(),
+                                 ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ProcessMouseDragged(sub_menu, drag_event_exit);
+  EXPECT_FALSE(first_item->IsSelected());
+
+  // Complete drag with release
+  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, release_location,
+                               release_location, ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ProcessMouseReleased(sub_menu, release_event);
+}
+
 #endif  // defined(USE_AURA)
 
 }  // namespace test
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 06be5f0..905690f 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -347,6 +347,10 @@
   return submenu_;
 }
 
+bool MenuItemView::SubmenuIsShowing() const {
+  return HasSubmenu() && GetSubmenu()->IsShowing();
+}
+
 void MenuItemView::SetTitle(const base::string16& title) {
   title_ = title;
   invalidate_dimensions();  // Triggers preferred size recalculation.
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index c80a8db..a23101c 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -222,6 +222,9 @@
   // Returns the view containing child menu items.
   virtual SubmenuView* GetSubmenu() const;
 
+  // Returns true if this menu item has a submenu and it is showing
+  virtual bool SubmenuIsShowing() const;
+
   // Returns the parent menu item.
   MenuItemView* GetParentMenuItem() { return parent_menu_item_; }
   const MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
index 8827041..016d93d7 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
@@ -118,12 +118,14 @@
    */
   setOldImageUrl(imageUrl) {
     this.oldImageUrl_ = imageUrl;
-    if (imageUrl)
+    if (imageUrl) {
       this.$.selector.select(this.$.selector.indexOf(this.$.oldImage));
-    else if (this.cameraSelected_)
+      this.selectedImageUrl_ = imageUrl;
+    } else if (this.cameraSelected_) {
       this.$.selector.select(this.$.selector.indexOf(this.$.cameraImage));
-    else if (this.fallbackImage_)
+    } else if (this.fallbackImage_) {
       this.selectImage_(this.fallbackImage_, true /* activate */);
+    }
   },
 
   /**
@@ -135,6 +137,7 @@
     if (!this.selectedItem ||
         this.selectedItem.dataset.type != CrPicture.SelectionTypes.CAMERA) {
       this.$.selector.select(this.$.selector.indexOf(image));
+      this.selectedItem = image;
     }
   },
 
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html
new file mode 100644
index 0000000..c82c822
--- /dev/null
+++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -0,0 +1,28 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+
+<!-- Common paper-button styling for Material Design WebUI. -->
+<dom-module id="paper-button-style">
+  <template>
+    <style>
+      paper-button {
+        --paper-button: {
+          -webkit-padding-end: var(--cr-button-edge-spacing);
+          -webkit-padding-start: var(--cr-button-edge-spacing);
+          color: var(--paper-grey-600);
+          font-weight: 500;
+          min-width: 1em;  /* A tighter fit than 5.14em for short buttons. */
+          text-decoration: none;
+        };
+        --paper-button-flat-keyboard-focus: {
+          background: rgba(0, 0, 0, .12);
+        };
+        flex-shrink: 0;
+        height: 36px;
+        margin: 0;
+      }
+    </style>
+  </template>
+</dom-module>
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index 4ce2020..224a9476 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -1,4 +1,5 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <!-- Common css variables for Material Design WebUI. -->
@@ -8,6 +9,8 @@
       cursor: pointer;
     };
 
+    --cr-button-edge-spacing: 12px;
+
     /* Spacing between policy (controlledBy) indicator and control. */
     --cr-controlled-by-spacing: 24px;
 
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index 4cd422e..d9db27b 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -176,6 +176,9 @@
              file="../../webui/resources/cr_elements/cr_icons_css.html"
              flattenhtml="true"
              type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML"
+             file="../../webui/resources/cr_elements/paper_button_style_css.html"
+             type="chrome_html" />
   <structure name="IDR_CR_ELEMENTS_CR_SHARED_VARS_CSS_HTML"
              file="../../webui/resources/cr_elements/shared_vars_css.html"
              type="chrome_html" />